From 1a7589e45ac60e6825a85d2143fb434c32a1f935 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 16 Oct 2025 17:59:52 -0700 Subject: [PATCH 1/6] Add Zed editor config --- .eslintrc.js | 10 +- .zed/settings.json | 116 +++++++++ example/App.tsx | 441 +++++++++++++++++++---------------- example/metro.config.js | 2 +- example/src/StreamScreen.tsx | 17 +- src/lib/XMTPPush.ts | 5 +- 6 files changed, 374 insertions(+), 217 deletions(-) create mode 100644 .zed/settings.json diff --git a/.eslintrc.js b/.eslintrc.js index e42a86b98..abb877b1c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,10 +1,10 @@ module.exports = { root: true, - parser: "@typescript-eslint/parser", + parser: '@typescript-eslint/parser', parserOptions: { - project: ["./tsconfig.json", "./example/tsconfig.json"] - }, - plugins: ["@typescript-eslint"], + project: ['./tsconfig.json', './example/tsconfig.json'], + }, + plugins: ['@typescript-eslint'], extends: ['universe/native', 'universe/web'], ignorePatterns: ['build'], plugins: ['prettier'], @@ -12,6 +12,6 @@ module.exports = { __dirname: true, }, rules: { - "@typescript-eslint/no-floating-promises": ["error"], + '@typescript-eslint/no-floating-promises': ['error'], }, } diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 000000000..a52b0f578 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,116 @@ +{ + // Formatter configuration + "formatter": { + "language_server": { + "name": "typescript-language-server" + } + }, + + // Language-specific settings + "languages": { + "TypeScript": { + "format_on_save": "on", + "formatter": [ + { + "code_action": "source.fixAll.eslint" + }, + // { + // "code_action": "source.organizeImports" + // }, + { + "external": { + "command": "npx", + "arguments": ["prettier", "--write", "--stdin-filepath", "{buffer_path}"] + } + } + ] + }, + "JavaScript": { + "format_on_save": "on", + "formatter": [ + { + "code_action": "source.fixAll.eslint" + }, + // { + // "code_action": "source.organizeImports" + // }, + { + "external": { + "command": "npx", + "arguments": ["prettier", "--write", "--stdin-filepath", "{buffer_path}"] + } + } + ] + }, + "TSX": { + "format_on_save": "on", + "formatter": [ + { + "code_action": "source.fixAll.eslint" + }, + // { + // "code_action": "source.organizeImports" + // }, + { + "external": { + "command": "npx", + "arguments": ["prettier", "--write", "--stdin-filepath", "{buffer_path}"] + } + } + ] + }, + "Swift": { + "format_on_save": "on", + "tab_size": 2, + "hard_tabs": false, + "formatter": { + "external": { + "command": "swiftformat", + "arguments": ["--quiet", "--stdinpath", "{buffer_path}"] + } + } + }, + "Kotlin": { + "format_on_save": "on", + "tab_size": 2, + "hard_tabs": false, + "formatter": { + "external": { + "command": "ktlint", + "arguments": ["--format", "--stdin"] + } + } + } + }, + + // ESLint integration + "lsp": { + "eslint": { + "settings": { + "codeActionOnSave": { + "enable": true, + "mode": "all" + }, + "format": false, + "validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"] + } + } + }, + + // File associations + "file_types": { + "TypeScript": ["ts"], + "TSX": ["tsx"], + "JavaScript": ["js", "cjs", "mjs"], + "Swift": ["swift"], + "Kotlin": ["kt", "kts"] + }, + + // Project-specific settings + "tab_size": 2, + "hard_tabs": false, + "remove_trailing_whitespace_on_save": true, + "ensure_final_newline_on_save": true, + + +} diff --git a/example/App.tsx b/example/App.tsx index ed0adc65d..55a907ab5 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,7 +1,7 @@ import { NavigationContainer } from '@react-navigation/native' // import { Ethereum } from '@thirdweb-dev/chains' -import 'react-native-get-random-values'; -import '@ethersproject/shims'; +import 'react-native-get-random-values' +import '@ethersproject/shims' import { Buffer as BufferPolyfill } from 'buffer' // Make Buffer globally available global.Buffer = global.Buffer || BufferPolyfill @@ -10,7 +10,17 @@ global.Buffer = global.Buffer || BufferPolyfill // metamaskWallet, // rainbowWallet, // } from '@thirdweb-dev/react-native' -import { Button, Platform, ActionSheetIOS, Modal, View, Text, TouchableOpacity, StyleSheet, FlatList } from 'react-native' +import { + Button, + Platform, + ActionSheetIOS, + Modal, + View, + Text, + TouchableOpacity, + StyleSheet, + FlatList, +} from 'react-native' import Config from 'react-native-config' // Used to polyfill webCrypto in react-native import { QueryClient, QueryClientProvider } from 'react-query' @@ -26,7 +36,7 @@ import LaunchScreen from './src/LaunchScreen' import { Navigator } from './src/Navigation' import StreamScreen from './src/StreamScreen' import TestScreen from './src/TestScreen' -import { LogLevel, LogRotation } from 'xmtp-react-native-sdk/lib/types/LogTypes'; +import { LogLevel, LogRotation } from 'xmtp-react-native-sdk/lib/types/LogTypes' const queryClient = new QueryClient() @@ -53,7 +63,7 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { const [fileSizes, setFileSizes] = useState>({}) const [isLoading, setIsLoading] = useState(false) const [logStatus, setLogStatus] = useState('') - + // Fetch log files when modal becomes visible useEffect(() => { if (visible) { @@ -62,62 +72,69 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { setIsLoading(true) setFileSizes({}) // Reset file sizes when reopening setLogStatus('Checking log status...') - + // Check if logging is active const isActive = await Client.isLogWriterActive() setLogStatus(`Logging ${isActive ? 'is active' : 'is NOT active'}`) - + const files = await Client.getXMTPLogFilePaths() console.log('Found log files:', files) setLogFiles(files) - + if (files.length === 0) { - setLogStatus(prev => `${prev}\nNo log files found. Try activating logs first.`) + setLogStatus( + (prev) => + `${prev}\nNo log files found. Try activating logs first.` + ) } else { - setLogStatus(prev => `${prev}\nFound ${files.length} log file(s)`) + setLogStatus((prev) => `${prev}\nFound ${files.length} log file(s)`) } - + // Load file sizes in parallel const sizePromises = files.map(async (path) => { try { const content = await Client.readXMTPLogFile(path) console.log(`Log file ${path} size: ${content.length} bytes`) - return { - path, + return { + path, size: `${(content.length / 1024).toFixed(2)} KB`, - isEmpty: content.length === 0 + isEmpty: content.length === 0, } } catch (error) { console.error(`Error loading size for ${path}:`, error) return { path, size: 'Error loading size', isEmpty: true } } }) - + const results = await Promise.all(sizePromises) - + // Update all sizes at once const newSizes: Record = {} let emptyCount = 0 - + results.forEach(({ path, size, isEmpty }) => { newSizes[path] = isEmpty ? `${size} (empty)` : size if (isEmpty) emptyCount++ }) - + setFileSizes(newSizes) - + if (emptyCount > 0) { - setLogStatus(prev => `${prev}\n${emptyCount} empty log file(s) found. Make sure logging is properly activated.`) + setLogStatus( + (prev) => + `${prev}\n${emptyCount} empty log file(s) found. Make sure logging is properly activated.` + ) } - } catch (error) { console.error('Error fetching log files:', error) - setLogStatus(`Error: ${error instanceof Error ? error.message : String(error)}`) + setLogStatus( + `Error: ${error instanceof Error ? error.message : String(error)}` + ) } finally { setIsLoading(false) } } - + fetchLogFiles() } }, [visible]) @@ -127,12 +144,12 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { try { // Read the log file content const content = await Client.readXMTPLogFile(filePath) - + if (content.length === 0) { alert('This log file is empty. Try generating some logs first.') return } - + // Use the Share API to open the native share dialog if (Platform.OS === 'ios' || Platform.OS === 'android') { const Share = require('react-native').Share @@ -140,7 +157,9 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { title: 'XMTP Log File', message: content, // On iOS, you can also specify a subject - ...(Platform.OS === 'ios' && { subject: `XMTP Log: ${filePath.split('/').pop()}` }) + ...(Platform.OS === 'ios' && { + subject: `XMTP Log: ${filePath.split('/').pop()}`, + }), }) } } catch (error) { @@ -159,14 +178,14 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { Log Files - + {/* Display log status information */} {logStatus ? ( {logStatus} ) : null} - + {isLoading ? ( Loading log files... ) : logFiles.length === 0 ? ( @@ -176,11 +195,15 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { data={logFiles} keyExtractor={(item) => item} renderItem={({ item }) => ( - shareLogFile(item)} > - + {item} @@ -191,7 +214,7 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { )} /> )} - + Close @@ -271,7 +294,11 @@ const styles = StyleSheet.create({ }) // Custom dropdown for Android -const AndroidDropdown: React.FC = ({ visible, onClose, options }) => { +const AndroidDropdown: React.FC = ({ + visible, + onClose, + options, +}) => { return ( = ({ visible, onClose, opt animationType="fade" onRequestClose={onClose} > - - = ({ visible, onClose, opt export default function App() { // Uncomment below to ensure correct id loaded from .env // console.log("Thirdweb client id: " + Config.THIRD_WEB_CLIENT_ID) - + const [showAndroidDropdown, setShowAndroidDropdown] = useState(false) const [showLogFilesModal, setShowLogFilesModal] = useState(false) - + // Function to clear all log files const clearLogFiles = async (): Promise => { try { @@ -334,9 +361,9 @@ export default function App() { alert('No log files to clear') return } - + let successCount = await Client.clearXMTPLogs() - + if (successCount === files.length) { alert('All log files cleared successfully') } else if (successCount > 0) { @@ -349,7 +376,7 @@ export default function App() { alert('Failed to clear log files') } } - + return ( // - - - - - - + + + + + + { + // Define dropdown options + const dropdownOptions: DropdownOption[] = [ + { + title: 'New Conversation', + onPress: () => navigation.navigate('conversationCreate'), }, - }} - /> - - { - // Define dropdown options - const dropdownOptions: DropdownOption[] = [ - { - title: 'New Conversation', - onPress: () => navigation.navigate('conversationCreate') - }, - { - title: 'Activate Logs', - onPress: async () => { - await Client.activatePersistentLibXMTPLogWriter(LogLevel.DEBUG, LogRotation.MINUTELY, 5) // Using default values - alert('Logs activated') - } - }, - { - title: 'Deactivate Logs', - onPress: () => { - Client.deactivatePersistentLibXMTPLogWriter() - alert('Logs deactivated') - } + { + title: 'Activate Logs', + onPress: async () => { + await Client.activatePersistentLibXMTPLogWriter( + LogLevel.DEBUG, + LogRotation.MINUTELY, + 5 + ) // Using default values + alert('Logs activated') }, - { - title: 'View Log Files', - onPress: () => setShowLogFilesModal(true) + }, + { + title: 'Deactivate Logs', + onPress: () => { + Client.deactivatePersistentLibXMTPLogWriter() + alert('Logs deactivated') }, - { - title: 'Clear Log Files', - onPress: () => { - // Show confirmation dialog before clearing logs - if (Platform.OS === 'ios') { - ActionSheetIOS.showActionSheetWithOptions( + }, + { + title: 'View Log Files', + onPress: () => setShowLogFilesModal(true), + }, + { + title: 'Clear Log Files', + onPress: () => { + // Show confirmation dialog before clearing logs + if (Platform.OS === 'ios') { + ActionSheetIOS.showActionSheetWithOptions( + { + options: ['Clear All Log Files', 'Cancel'], + destructiveButtonIndex: 0, + cancelButtonIndex: 1, + title: + 'Are you sure you want to delete all log files?', + message: 'This action cannot be undone.', + }, + (buttonIndex) => { + if (buttonIndex === 0) { + clearLogFiles() + } + } + ) + } else { + // For Android, use Alert.alert + const Alert = require('react-native').Alert + Alert.alert( + 'Clear Log Files', + 'Are you sure you want to delete all log files? This action cannot be undone.', + [ { - options: ['Clear All Log Files', 'Cancel'], - destructiveButtonIndex: 0, - cancelButtonIndex: 1, - title: 'Are you sure you want to delete all log files?', - message: 'This action cannot be undone.' + text: 'Cancel', + style: 'cancel', }, - (buttonIndex) => { - if (buttonIndex === 0) { - clearLogFiles() - } - } - ) - } else { - // For Android, use Alert.alert - const Alert = require('react-native').Alert - Alert.alert( - 'Clear Log Files', - 'Are you sure you want to delete all log files? This action cannot be undone.', - [ - { - text: 'Cancel', - style: 'cancel' - }, - { - text: 'Clear', - onPress: clearLogFiles, - style: 'destructive' - } - ] - ) - } + { + text: 'Clear', + onPress: clearLogFiles, + style: 'destructive', + }, + ] + ) } - } - ] - - // Platform-specific dropdown handling - const showDropdown = (): void => { - if (Platform.OS === 'ios') { - ActionSheetIOS.showActionSheetWithOptions( - { - options: [...dropdownOptions.map(option => option.title), 'Cancel'], - cancelButtonIndex: dropdownOptions.length, - }, - (buttonIndex) => { - if (buttonIndex < dropdownOptions.length) { - dropdownOptions[buttonIndex].onPress() - } - } - ) - } else { - setShowAndroidDropdown(true) - } - } - - return { - title: 'My Conversations', - headerStyle: { - backgroundColor: 'rgb(49 0 110)', }, - headerTintColor: '#fff', - headerTitleStyle: { - fontWeight: 'bold', - }, - headerRight: () => ( - <> -