بنقرة واحدة
بنقرة واحدة
Add a new feature or screen to the Ecency mobile app following established patterns
Add a new blockchain mutation wrapper using @ecency/sdk in the mobile app
Use an @ecency/sdk query in the mobile app or create a new app-specific query
Review code changes for bugs, pattern violations, and common pitfalls in ecency-mobile
Debug common issues in the Ecency mobile app with known solutions and investigation patterns
| name | add-sheet |
| description | Add a new bottom sheet (action sheet) to the mobile app |
| argument-hint | ["sheet-name"] |
| disable-model-invocation | true |
Create a new bottom sheet using react-native-actions-sheet.
Location: src/components/<sheetName>/<sheetName>.tsx
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { useIntl } from 'react-intl';
import ActionSheet, { SheetManager, SheetProps } from 'react-native-actions-sheet';
import EStyleSheet from 'react-native-extended-stylesheet';
import { MainButton } from '../mainButton';
const MySheet: React.FC<SheetProps<'my_sheet'>> = ({ sheetId, payload }) => {
const intl = useIntl();
// IMPORTANT: react-native-actions-sheet keeps registered sheets mounted.
// State persists between invocations. Reset in useEffect if needed:
// useEffect(() => { resetState(); }, [payload]);
const _handleConfirm = () => {
// Return a value to the caller
SheetManager.hide(sheetId, { payload: 'result_value' });
};
const _handleCancel = () => {
// Return false/undefined to indicate cancellation
SheetManager.hide(sheetId, { payload: false });
};
return (
<ActionSheet id={sheetId}>
<View style={styles.container}>
<Text style={styles.title}>
{intl.formatMessage({ id: 'my_sheet.title' })}
</Text>
<MainButton
text={intl.formatMessage({ id: 'my_sheet.confirm' })}
onPress={_handleConfirm}
/>
</View>
</ActionSheet>
);
};
const styles = EStyleSheet.create({
container: {
padding: 16,
backgroundColor: '$primaryBackgroundColor',
},
title: {
fontSize: 18,
fontWeight: 'bold',
color: '$primaryBlack',
},
});
export default MySheet;
Location: src/components/<sheetName>/index.ts
export { default as MySheet } from './<sheetName>';
Add to src/components/index.tsx:
export { MySheet } from './<sheetName>';
In src/navigation/sheets.tsx:
import { MySheet } from '../components';
SheetNames enum:export enum SheetNames {
// ... existing
MY_SHEET = 'my_sheet',
}
registerSheet(SheetNames.MY_SHEET, MySheet);
declare module 'react-native-actions-sheet' {
interface Sheets {
// ... existing
[SheetNames.MY_SHEET]: SheetDefinition<{
payload: {
someParam: string;
};
returnValue: string | false; // what hide() returns
}>;
}
}
From anywhere in the app:
import { SheetManager } from 'react-native-actions-sheet';
import { SheetNames } from '../navigation/sheets';
// Async — waits for sheet to close and returns the result
const result = await SheetManager.show(SheetNames.MY_SHEET, {
payload: { someParam: 'value' },
});
if (result) {
// User confirmed
} else {
// User cancelled (backdrop tap or explicit cancel)
}
In src/config/locales/en-US.json:
{
"my_sheet.title": "Sheet Title",
"my_sheet.confirm": "Confirm",
"my_sheet.cancel": "Cancel"
}
$primaryBackgroundColor for backgrounds (supports dark mode)$primaryBlack for text (adapts to theme)$iconColor for iconsEStyleSheet from react-native-extended-stylesheetSheets stay mounted. If your sheet has state, reset it when payload changes:
useEffect(() => {
setInput('');
setError('');
setLoading(false);
}, [payload]);
SheetManager.hide(sheetId, { payload: value }) to return values — the show() promise resolves with this value.undefined — handle this as cancellation in the caller.useIntl for strings — not hardcoded text.$variables for colors, not hardcoded values.