Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
52 changes: 36 additions & 16 deletions src/ui/HomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,7 @@ const HomeView: React.FC<HomeViewProps> = ({ onNavigate }) => {

const canAccessProposals = !!(
config.network &&
config.multisig &&
config.profile &&
filteredProfiles.some(p => p.name === config.profile)
config.multisig
);

const networks = NETWORK_CHOICES;
Expand Down Expand Up @@ -241,10 +239,18 @@ const HomeView: React.FC<HomeViewProps> = ({ onNavigate }) => {
} else if (key.upArrow) {
setMenu(m => ({ ...m, subIndex: Math.max(0, m.subIndex - 1) }));
} else if (key.downArrow) {
setMenu(m => ({ ...m, subIndex: Math.min(filteredProfiles.length - 1, m.subIndex + 1) }));
} else if (key.return && filteredProfiles[subIndex]) {
updateConfig({ profile: filteredProfiles[subIndex].name });
collapseMenu();
// Allow navigating to "Skip" option (one past the last profile)
setMenu(m => ({ ...m, subIndex: Math.min(filteredProfiles.length, m.subIndex + 1) }));
} else if (key.return) {
if (subIndex === filteredProfiles.length) {
// "Skip" option selected - clear profile for read-only mode
updateConfig({ profile: undefined });
collapseMenu();
} else if (filteredProfiles[subIndex]) {
// Profile selected
updateConfig({ profile: filteredProfiles[subIndex].name });
collapseMenu();
}
}
} else if (expandedItem === 'multisig') {
if (key.escape) {
Expand Down Expand Up @@ -321,7 +327,7 @@ const HomeView: React.FC<HomeViewProps> = ({ onNavigate }) => {
if (view === 'proposal' && canAccessProposals && config.network) {
return (
<ProposalView
profile={config.profile!}
profile={config.profile}
multisigAddress={config.multisig!}
network={config.network}
onBack={() => setView('home')}
Expand Down Expand Up @@ -394,14 +400,16 @@ const HomeView: React.FC<HomeViewProps> = ({ onNavigate }) => {
isSelected={selectedIndex === 2}
label="Profile"
value={
filteredProfiles.some(p => p.name === config.profile)
config.profile === undefined && config.network && config.multisig
? 'Read-only'
: filteredProfiles.some(p => p.name === config.profile)
? `${config.profile}${!isProfileOwner ? ' (non-owner)' : ''}`
: undefined
}
placeholder={
!config.network ? "(Select network first)" :
filteredProfiles.length === 0 ? `(No profiles for ${config.network})` :
"(Select profile)"
"(Select profile or skip)"
}
disabled={!config.network}
warning={!!config.profile && !isProfileOwner}
Expand Down Expand Up @@ -525,14 +533,26 @@ const ProfileExpanded: React.FC<{
<>
<Text>{isSelected ? '▼' : ' '} Profile:</Text>
{profiles.length > 0 ? (
profiles.map((profile, index) => (
<Text key={profile.name}>
{' '}{index === subIndex ? '▶' : ' '} {profile.name}
{profile.name === currentProfile && <Text color="green"> ✓</Text>}
<>
{profiles.map((profile, index) => (
<Text key={profile.name}>
{' '}{index === subIndex ? '▶' : ' '} {profile.name}
{profile.name === currentProfile && <Text color="green"> ✓</Text>}
</Text>
))}
<Text>
{' '}{subIndex === profiles.length ? '▶' : ' '} <Text dimColor>Skip (read-only)</Text>
{currentProfile === undefined && <Text color="green"> ✓</Text>}
</Text>
))
</>
) : (
<Text dimColor> No profiles found for {network}</Text>
<>
<Text dimColor> No profiles found for {network}</Text>
<Text>
{' '}{subIndex === 0 ? '▶' : ' '} <Text dimColor>Skip (read-only)</Text>
{currentProfile === undefined && <Text color="green"> ✓</Text>}
</Text>
</>
)}
</>
);
Expand Down
39 changes: 26 additions & 13 deletions src/ui/ProposalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ interface ProposalViewProps {
multisigAddress: string;
network: string;
fullnode?: string;
profile: string;
profile?: string;
sequenceNumber?: number;
onBack?: () => void;
}
Expand Down Expand Up @@ -117,11 +117,20 @@ const ProposalView: React.FC<ProposalViewProps> = ({
useEffect(() => {
const init = async () => {
try {
const profileData = await loadProfile(profile, network as NetworkChoice);
const { signer } = profileData;
setSignerAddress(signer.accountAddress.toString());

const aptosInstance = initAptos(network as NetworkChoice, fullnode || profileData.fullnode);
let fullnodeUrl = fullnode;

// Load profile if provided
if (profile) {
const profileData = await loadProfile(profile, network as NetworkChoice);
const { signer } = profileData;
setSignerAddress(signer.accountAddress.toString());
fullnodeUrl = fullnodeUrl || profileData.fullnode;
} else {
// Read-only mode - no profile
setSignerAddress('');
}

const aptosInstance = initAptos(network as NetworkChoice, fullnodeUrl);
setAptos(aptosInstance);

// Get multisig info
Expand Down Expand Up @@ -266,7 +275,7 @@ const ProposalView: React.FC<ProposalViewProps> = ({
approved,
multisigAddress,
network as NetworkChoice,
profile
profile!
);
setActionMessage(chalk.green(`✅ Vote submitted: ${getExplorerUrl(network as NetworkChoice, `txn/${hash}`)}`));
await fetchProposals();
Expand All @@ -282,7 +291,7 @@ const ProposalView: React.FC<ProposalViewProps> = ({
const actionPast = reject ? 'Reject' : 'Execute';
setConfirmAction(null); // Clear confirmation immediately
setActionMessage(chalk.yellow(`⏳ ${action} transaction... Please wait while the transaction is submitted to the blockchain.`));
const result = await handleExecuteCommand(multisigAddress, profile, network as NetworkChoice, reject, true);
const result = await handleExecuteCommand(multisigAddress, profile!, network as NetworkChoice, reject, true);
if (reject || result.success) {
setActionMessage(chalk.green(`✅ ${actionPast} successful: ${getExplorerUrl(network as NetworkChoice, `txn/${result.hash}`)}`));
} else {
Expand Down Expand Up @@ -330,15 +339,16 @@ const ProposalView: React.FC<ProposalViewProps> = ({
} else if (key.return) {
// Toggle expand for current selection only
setIsSelectedExpanded(prev => !prev);
} else if ((normalizedInput === 'y' || normalizedInput === 'n') && proposals[selectedIndex]) {
} else if ((normalizedInput === 'y' || normalizedInput === 'n') && proposals[selectedIndex] && profile) {
// Only allow voting if profile is set
handleVote(proposals[selectedIndex].sequenceNumber, normalizedInput === 'y');
} else if (normalizedInput === 'e' && proposals[selectedIndex]) {
// Only allow execute if this is the smallest sequence number
} else if (normalizedInput === 'e' && proposals[selectedIndex] && profile) {
// Only allow execute if profile is set and this is the smallest sequence number
if (proposals[selectedIndex].canExecute && selectedIndex === 0) {
showConfirmation('execute', proposals[selectedIndex].sequenceNumber);
}
} else if (normalizedInput === 'r' && proposals[selectedIndex]) {
// Only allow reject if this is the smallest sequence number
} else if (normalizedInput === 'r' && proposals[selectedIndex] && profile) {
// Only allow reject if profile is set and this is the smallest sequence number
if (proposals[selectedIndex].canReject && selectedIndex === 0) {
showConfirmation('reject', proposals[selectedIndex].sequenceNumber);
}
Expand Down Expand Up @@ -454,6 +464,9 @@ const ProposalView: React.FC<ProposalViewProps> = ({
{proposals[selectedIndex] && (
<>
#{proposals[selectedIndex].sequenceNumber}: {(() => {
if (!profile) {
return '(Read-only) ';
}
const p = proposals[selectedIndex];
const isSmallest = selectedIndex === 0;
let actions = '[Y]es [N]o ';
Expand Down
Loading