mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-12-11 11:52:35 -05:00
Compare commits
33 Commits
veganbeef/
...
sc/revert-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf563154ca | ||
|
|
196378daeb | ||
|
|
89f82253ca | ||
|
|
c786cabe84 | ||
|
|
760efdb96b | ||
|
|
5fd0e21532 | ||
|
|
0d43b35c28 | ||
|
|
349cdea489 | ||
|
|
181c364de4 | ||
|
|
78626c2dc7 | ||
|
|
b72198575a | ||
|
|
b1fdfc19a9 | ||
|
|
4ba9480832 | ||
|
|
378ea65154 | ||
|
|
b366d97b53 | ||
|
|
16c433a13c | ||
|
|
b9e2087bd8 | ||
|
|
86029b2bd9 | ||
|
|
5fbd9c5c09 | ||
|
|
3815f45b44 | ||
|
|
c713d53054 | ||
|
|
d10bee4d4c | ||
|
|
44d0c6f905 | ||
|
|
4f308d3f07 | ||
|
|
00fdd21044 | ||
|
|
8475b28107 | ||
|
|
e74b2581df | ||
|
|
505aa54b16 | ||
|
|
8de3f598b7 | ||
|
|
65419a76ee | ||
|
|
6b0350d477 | ||
|
|
2a1a3d7c40 | ||
|
|
193dffe03a |
16
bin/init.js
16
bin/init.js
@@ -546,19 +546,19 @@ export async function init(projectName = null, autoAcceptDefaults = false, apiKe
|
|||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regex patterns that match whole lines with export const
|
// Regex patterns that match whole lines with export const (with TypeScript types)
|
||||||
const patterns = {
|
const patterns = {
|
||||||
APP_NAME: /^export const APP_NAME\s*=\s*['"`][^'"`]*['"`];$/m,
|
APP_NAME: /^export const APP_NAME\s*:\s*string\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||||
APP_DESCRIPTION:
|
APP_DESCRIPTION:
|
||||||
/^export const APP_DESCRIPTION\s*=\s*['"`][^'"`]*['"`];$/m,
|
/^export const APP_DESCRIPTION\s*:\s*string\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||||
APP_PRIMARY_CATEGORY:
|
APP_PRIMARY_CATEGORY:
|
||||||
/^export const APP_PRIMARY_CATEGORY\s*=\s*['"`][^'"`]*['"`];$/m,
|
/^export const APP_PRIMARY_CATEGORY\s*:\s*string\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||||
APP_TAGS: /^export const APP_TAGS\s*=\s*\[[^\]]*\];$/m,
|
APP_TAGS: /^export const APP_TAGS\s*:\s*string\[\]\s*=\s*\[[^\]]*\];$/m,
|
||||||
APP_BUTTON_TEXT:
|
APP_BUTTON_TEXT:
|
||||||
/^export const APP_BUTTON_TEXT\s*=\s*['"`][^'"`]*['"`];$/m,
|
/^export const APP_BUTTON_TEXT\s*:\s*string\s*=\s*['"`][^'"`]*['"`];$/m,
|
||||||
USE_WALLET: /^export const USE_WALLET\s*=\s*(true|false);$/m,
|
USE_WALLET: /^export const USE_WALLET\s*:\s*boolean\s*=\s*(true|false);$/m,
|
||||||
ANALYTICS_ENABLED:
|
ANALYTICS_ENABLED:
|
||||||
/^export const ANALYTICS_ENABLED\s*=\s*(true|false);$/m,
|
/^export const ANALYTICS_ENABLED\s*:\s*boolean\s*=\s*(true|false);$/m,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update APP_NAME
|
// Update APP_NAME
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@neynar/create-farcaster-mini-app",
|
"name": "@neynar/create-farcaster-mini-app",
|
||||||
"version": "1.6.0",
|
"version": "1.6.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": false,
|
"private": false,
|
||||||
"access": "public",
|
"access": "public",
|
||||||
|
|||||||
@@ -890,4 +890,4 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
@@ -118,7 +118,7 @@ export function AuthDialog({
|
|||||||
const content = getStepContent();
|
const content = getStepContent();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
|
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-xl w-full max-w-md shadow-2xl border border-gray-200 dark:border-gray-700 max-h-[80vh] sm:max-h-[90vh] flex flex-col">
|
<div className="bg-white dark:bg-gray-800 rounded-xl w-full max-w-md shadow-2xl border border-gray-200 dark:border-gray-700 max-h-[80vh] sm:max-h-[90vh] flex flex-col">
|
||||||
<div className="flex justify-between items-center p-4 sm:p-6 pb-3 sm:pb-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0">
|
<div className="flex justify-between items-center p-4 sm:p-6 pb-3 sm:pb-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0">
|
||||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||||
@@ -204,7 +204,7 @@ export function AuthDialog({
|
|||||||
'https://farcaster.xyz/~/connect'
|
'https://farcaster.xyz/~/connect'
|
||||||
),
|
),
|
||||||
'_blank'
|
'_blank'
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn btn-outline flex items-center justify-center gap-2 w-full"
|
className="btn btn-outline flex items-center justify-center gap-2 w-full"
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ export function NeynarAuthButton() {
|
|||||||
setPollingInterval(null);
|
setPollingInterval(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/auth/signer?signerUuid=${signerUuid}`
|
`/api/auth/signer?signerUuid=${signerUuid}`
|
||||||
@@ -321,7 +321,7 @@ export function NeynarAuthButton() {
|
|||||||
setPollingInterval(null);
|
setPollingInterval(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment retry count for other errors
|
// Increment retry count for other errors
|
||||||
retryCount++;
|
retryCount++;
|
||||||
if (retryCount >= maxRetries) {
|
if (retryCount >= maxRetries) {
|
||||||
@@ -329,7 +329,7 @@ export function NeynarAuthButton() {
|
|||||||
setPollingInterval(null);
|
setPollingInterval(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Failed to poll signer status: ${response.status}`);
|
throw new Error(`Failed to poll signer status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ export function NeynarAuthButton() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMessage(data?.message || null);
|
setMessage(data?.message || null);
|
||||||
setSignature(data?.signature || null);
|
setSignature(data?.signature || null);
|
||||||
|
|
||||||
// Reset the signer flow flag when message/signature change
|
// Reset the signer flow flag when message/signature change
|
||||||
if (data?.message && data?.signature) {
|
if (data?.message && data?.signature) {
|
||||||
signerFlowStartedRef.current = false;
|
signerFlowStartedRef.current = false;
|
||||||
@@ -459,9 +459,14 @@ export function NeynarAuthButton() {
|
|||||||
|
|
||||||
// Handle fetching signers after successful authentication
|
// Handle fetching signers after successful authentication
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (message && signature && !isSignerFlowRunning && !signerFlowStartedRef.current) {
|
if (
|
||||||
|
message &&
|
||||||
|
signature &&
|
||||||
|
!isSignerFlowRunning &&
|
||||||
|
!signerFlowStartedRef.current
|
||||||
|
) {
|
||||||
signerFlowStartedRef.current = true;
|
signerFlowStartedRef.current = true;
|
||||||
|
|
||||||
const handleSignerFlow = async () => {
|
const handleSignerFlow = async () => {
|
||||||
setIsSignerFlowRunning(true);
|
setIsSignerFlowRunning(true);
|
||||||
try {
|
try {
|
||||||
@@ -479,7 +484,7 @@ export function NeynarAuthButton() {
|
|||||||
|
|
||||||
// First, fetch existing signers
|
// First, fetch existing signers
|
||||||
const signers = await fetchAllSigners(message, signature);
|
const signers = await fetchAllSigners(message, signature);
|
||||||
|
|
||||||
if (useBackendFlow && isMobileContext) setSignersLoading(true);
|
if (useBackendFlow && isMobileContext) setSignersLoading(true);
|
||||||
|
|
||||||
// Check if no signers exist or if we have empty signers
|
// Check if no signers exist or if we have empty signers
|
||||||
@@ -604,7 +609,7 @@ export function NeynarAuthButton() {
|
|||||||
clearInterval(pollingInterval);
|
clearInterval(pollingInterval);
|
||||||
setPollingInterval(null);
|
setPollingInterval(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset signer flow flag
|
// Reset signer flow flag
|
||||||
signerFlowStartedRef.current = false;
|
signerFlowStartedRef.current = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from 'react';
|
||||||
import { useMiniApp } from "@neynar/react";
|
import { useMiniApp } from '@neynar/react';
|
||||||
import { ShareButton } from "../Share";
|
import { ShareButton } from '../Share';
|
||||||
import { Button } from "../Button";
|
import { Button } from '../Button';
|
||||||
import { SignIn } from "../wallet/SignIn";
|
import { SignIn } from '../wallet/SignIn';
|
||||||
import { type Haptics } from "@farcaster/miniapp-sdk";
|
import { type Haptics } from '@farcaster/miniapp-sdk';
|
||||||
import { APP_URL } from "~/lib/constants";
|
import { APP_URL } from '~/lib/constants';
|
||||||
import { NeynarAuthButton } from '../NeynarAuthButton/index';
|
import { NeynarAuthButton } from '../NeynarAuthButton/index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,16 +124,16 @@ export function ActionsTab() {
|
|||||||
|
|
||||||
// --- Render ---
|
// --- Render ---
|
||||||
return (
|
return (
|
||||||
<div className='space-y-3 px-6 w-full max-w-md mx-auto'>
|
<div className="space-y-3 px-6 w-full max-w-md mx-auto">
|
||||||
{/* Share functionality */}
|
{/* Share functionality */}
|
||||||
<ShareButton
|
<ShareButton
|
||||||
buttonText='Share Mini App'
|
buttonText="Share Mini App"
|
||||||
cast={{
|
cast={{
|
||||||
text: 'Check out this awesome frame @1 @2 @3! 🚀🪐',
|
text: 'Check out this awesome frame @1 @2 @3! 🚀🪐',
|
||||||
bestFriends: true,
|
bestFriends: true,
|
||||||
embeds: [`${APP_URL}/share/${context?.user?.fid || ''}`]
|
embeds: [`${APP_URL}/share/${context?.user?.fid || ''}`],
|
||||||
}}
|
}}
|
||||||
className='w-full'
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Authentication */}
|
{/* Authentication */}
|
||||||
@@ -147,25 +147,25 @@ export function ActionsTab() {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
actions.openUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
|
actions.openUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
|
||||||
}
|
}
|
||||||
className='w-full'
|
className="w-full"
|
||||||
>
|
>
|
||||||
Open Link
|
Open Link
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button onClick={actions.addMiniApp} disabled={added} className='w-full'>
|
<Button onClick={actions.addMiniApp} disabled={added} className="w-full">
|
||||||
Add Mini App to Client
|
Add Mini App to Client
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Notification functionality */}
|
{/* Notification functionality */}
|
||||||
{notificationState.sendStatus && (
|
{notificationState.sendStatus && (
|
||||||
<div className='text-sm w-full'>
|
<div className="text-sm w-full">
|
||||||
Send notification result: {notificationState.sendStatus}
|
Send notification result: {notificationState.sendStatus}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
onClick={sendFarcasterNotification}
|
onClick={sendFarcasterNotification}
|
||||||
disabled={!notificationDetails}
|
disabled={!notificationDetails}
|
||||||
className='w-full'
|
className="w-full"
|
||||||
>
|
>
|
||||||
Send notification
|
Send notification
|
||||||
</Button>
|
</Button>
|
||||||
@@ -174,14 +174,14 @@ export function ActionsTab() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={copyUserShareUrl}
|
onClick={copyUserShareUrl}
|
||||||
disabled={!context?.user?.fid}
|
disabled={!context?.user?.fid}
|
||||||
className='w-full'
|
className="w-full"
|
||||||
>
|
>
|
||||||
{notificationState.shareUrlCopied ? 'Copied!' : 'Copy share URL'}
|
{notificationState.shareUrlCopied ? 'Copied!' : 'Copy share URL'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Haptic feedback controls */}
|
{/* Haptic feedback controls */}
|
||||||
<div className='space-y-2'>
|
<div className="space-y-2">
|
||||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300'>
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
Haptic Intensity
|
Haptic Intensity
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@@ -191,7 +191,7 @@ export function ActionsTab() {
|
|||||||
e.target.value as Haptics.ImpactOccurredType
|
e.target.value as Haptics.ImpactOccurredType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-primary'
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-primary"
|
||||||
>
|
>
|
||||||
<option value={'light'}>Light</option>
|
<option value={'light'}>Light</option>
|
||||||
<option value={'medium'}>Medium</option>
|
<option value={'medium'}>Medium</option>
|
||||||
@@ -199,7 +199,7 @@ export function ActionsTab() {
|
|||||||
<option value={'soft'}>Soft</option>
|
<option value={'soft'}>Soft</option>
|
||||||
<option value={'rigid'}>Rigid</option>
|
<option value={'rigid'}>Rigid</option>
|
||||||
</select>
|
</select>
|
||||||
<Button onClick={triggerHapticFeedback} className='w-full'>
|
<Button onClick={triggerHapticFeedback} className="w-full">
|
||||||
Trigger Haptic Feedback
|
Trigger Haptic Feedback
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user