mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-12-11 11:52:35 -05:00
Revert "Merge pull request #15 from neynarxyz/shreyas-formatting"
This reverts commitb1fdfc19a9, reversing changes made tob9e2087bd8.
This commit is contained in:
@@ -65,15 +65,14 @@ export const APP_SPLASH_URL: string = `${APP_URL}/splash.png`;
|
||||
* Background color for the splash screen.
|
||||
* Used as fallback when splash image is loading.
|
||||
*/
|
||||
export const APP_SPLASH_BACKGROUND_COLOR: string = '#f7f7f7';
|
||||
export const APP_SPLASH_BACKGROUND_COLOR: string = "#f7f7f7";
|
||||
|
||||
/**
|
||||
* Account association for the mini app.
|
||||
* Used to associate the mini app with a Farcaster account.
|
||||
* If not provided, the mini app will be unsigned and have limited capabilities.
|
||||
*/
|
||||
export const APP_ACCOUNT_ASSOCIATION: AccountAssociation | undefined =
|
||||
undefined;
|
||||
export const APP_ACCOUNT_ASSOCIATION: AccountAssociation | undefined = undefined;
|
||||
|
||||
// --- UI Configuration ---
|
||||
/**
|
||||
@@ -90,8 +89,7 @@ export const APP_BUTTON_TEXT: string = 'Launch NSK';
|
||||
* Neynar webhook endpoint. Otherwise, falls back to a local webhook
|
||||
* endpoint for development and testing.
|
||||
*/
|
||||
export const APP_WEBHOOK_URL: string =
|
||||
process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID
|
||||
export const APP_WEBHOOK_URL: string = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID
|
||||
? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event`
|
||||
: `${APP_URL}/api/webhook`;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type ReactElement } from 'react';
|
||||
import { BaseError, UserRejectedRequestError } from 'viem';
|
||||
import { type ReactElement } from "react";
|
||||
import { BaseError, UserRejectedRequestError } from "viem";
|
||||
|
||||
/**
|
||||
* Renders an error object in a user-friendly format.
|
||||
*
|
||||
*
|
||||
* This utility function takes an error object and renders it as a React element
|
||||
* with consistent styling. It handles different types of errors including:
|
||||
* - Error objects with message properties
|
||||
@@ -11,14 +11,14 @@ import { BaseError, UserRejectedRequestError } from 'viem';
|
||||
* - String errors
|
||||
* - Unknown error types
|
||||
* - User rejection errors (special handling for wallet rejections)
|
||||
*
|
||||
*
|
||||
* The rendered error is displayed in a gray container with monospace font
|
||||
* for better readability of technical error details. User rejections are
|
||||
* displayed with a simpler, more user-friendly message.
|
||||
*
|
||||
*
|
||||
* @param error - The error object to render
|
||||
* @returns ReactElement - A styled error display component, or null if no error
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* {isError && renderError(error)}
|
||||
@@ -27,11 +27,11 @@ import { BaseError, UserRejectedRequestError } from 'viem';
|
||||
export function renderError(error: unknown): ReactElement | null {
|
||||
// Handle null/undefined errors
|
||||
if (!error) return null;
|
||||
|
||||
|
||||
// Special handling for user rejections in wallet operations
|
||||
if (error instanceof BaseError) {
|
||||
const isUserRejection = error.walk(
|
||||
e => e instanceof UserRejectedRequestError,
|
||||
(e) => e instanceof UserRejectedRequestError
|
||||
);
|
||||
|
||||
if (isUserRejection) {
|
||||
@@ -43,10 +43,10 @@ export function renderError(error: unknown): ReactElement | null {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Extract error message from different error types
|
||||
let errorMessage: string;
|
||||
|
||||
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message;
|
||||
} else if (typeof error === 'object' && error !== null && 'error' in error) {
|
||||
@@ -63,4 +63,4 @@ export function renderError(error: unknown): ReactElement | null {
|
||||
<div className="whitespace-pre-wrap break-words">{errorMessage}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
import { FrameNotificationDetails } from '@farcaster/miniapp-sdk';
|
||||
import { Redis } from '@upstash/redis';
|
||||
import { APP_NAME } from './constants';
|
||||
import { FrameNotificationDetails } from "@farcaster/miniapp-sdk";
|
||||
import { Redis } from "@upstash/redis";
|
||||
import { APP_NAME } from "./constants";
|
||||
|
||||
// In-memory fallback storage
|
||||
const localStore = new Map<string, FrameNotificationDetails>();
|
||||
|
||||
// Use Redis if KV env vars are present, otherwise use in-memory
|
||||
const useRedis = process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN;
|
||||
const redis = useRedis
|
||||
? new Redis({
|
||||
url: process.env.KV_REST_API_URL!,
|
||||
token: process.env.KV_REST_API_TOKEN!,
|
||||
})
|
||||
: null;
|
||||
const redis = useRedis ? new Redis({
|
||||
url: process.env.KV_REST_API_URL!,
|
||||
token: process.env.KV_REST_API_TOKEN!,
|
||||
}) : null;
|
||||
|
||||
function getUserNotificationDetailsKey(fid: number): string {
|
||||
return `${APP_NAME}:user:${fid}`;
|
||||
}
|
||||
|
||||
export async function getUserNotificationDetails(
|
||||
fid: number,
|
||||
fid: number
|
||||
): Promise<FrameNotificationDetails | null> {
|
||||
const key = getUserNotificationDetailsKey(fid);
|
||||
if (redis) {
|
||||
@@ -30,7 +28,7 @@ export async function getUserNotificationDetails(
|
||||
|
||||
export async function setUserNotificationDetails(
|
||||
fid: number,
|
||||
notificationDetails: FrameNotificationDetails,
|
||||
notificationDetails: FrameNotificationDetails
|
||||
): Promise<void> {
|
||||
const key = getUserNotificationDetailsKey(fid);
|
||||
if (redis) {
|
||||
@@ -41,7 +39,7 @@ export async function setUserNotificationDetails(
|
||||
}
|
||||
|
||||
export async function deleteUserNotificationDetails(
|
||||
fid: number,
|
||||
fid: number
|
||||
): Promise<void> {
|
||||
const key = getUserNotificationDetailsKey(fid);
|
||||
if (redis) {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import {
|
||||
NeynarAPIClient,
|
||||
Configuration,
|
||||
WebhookUserCreated,
|
||||
} from '@neynar/nodejs-sdk';
|
||||
import { NeynarAPIClient, Configuration, WebhookUserCreated } from '@neynar/nodejs-sdk';
|
||||
import { APP_URL } from './constants';
|
||||
|
||||
let neynarClient: NeynarAPIClient | null = null;
|
||||
|
||||
// Example usage:
|
||||
// const client = getNeynarClient();
|
||||
// const user = await client.lookupUserByFid(fid);
|
||||
// const user = await client.lookupUserByFid(fid);
|
||||
export function getNeynarClient() {
|
||||
if (!neynarClient) {
|
||||
const apiKey = process.env.NEYNAR_API_KEY;
|
||||
@@ -37,12 +33,12 @@ export async function getNeynarUser(fid: number): Promise<User | null> {
|
||||
|
||||
type SendMiniAppNotificationResult =
|
||||
| {
|
||||
state: 'error';
|
||||
state: "error";
|
||||
error: unknown;
|
||||
}
|
||||
| { state: 'no_token' }
|
||||
| { state: 'rate_limit' }
|
||||
| { state: 'success' };
|
||||
| { state: "no_token" }
|
||||
| { state: "rate_limit" }
|
||||
| { state: "success" };
|
||||
|
||||
export async function sendNeynarMiniAppNotification({
|
||||
fid,
|
||||
@@ -62,19 +58,19 @@ export async function sendNeynarMiniAppNotification({
|
||||
target_url: APP_URL,
|
||||
};
|
||||
|
||||
const result = await client.publishFrameNotifications({
|
||||
targetFids,
|
||||
notification,
|
||||
const result = await client.publishFrameNotifications({
|
||||
targetFids,
|
||||
notification
|
||||
});
|
||||
|
||||
if (result.notification_deliveries.length > 0) {
|
||||
return { state: 'success' };
|
||||
return { state: "success" };
|
||||
} else if (result.notification_deliveries.length === 0) {
|
||||
return { state: 'no_token' };
|
||||
return { state: "no_token" };
|
||||
} else {
|
||||
return { state: 'error', error: result || 'Unknown error' };
|
||||
return { state: "error", error: result || "Unknown error" };
|
||||
}
|
||||
} catch (error) {
|
||||
return { state: 'error', error };
|
||||
return { state: "error", error };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import {
|
||||
SendNotificationRequest,
|
||||
sendNotificationResponseSchema,
|
||||
} from '@farcaster/miniapp-sdk';
|
||||
import { getUserNotificationDetails } from '~/lib/kv';
|
||||
import { APP_URL } from './constants';
|
||||
} from "@farcaster/miniapp-sdk";
|
||||
import { getUserNotificationDetails } from "~/lib/kv";
|
||||
import { APP_URL } from "./constants";
|
||||
|
||||
type SendMiniAppNotificationResult =
|
||||
| {
|
||||
state: 'error';
|
||||
state: "error";
|
||||
error: unknown;
|
||||
}
|
||||
| { state: 'no_token' }
|
||||
| { state: 'rate_limit' }
|
||||
| { state: 'success' };
|
||||
| { state: "no_token" }
|
||||
| { state: "rate_limit" }
|
||||
| { state: "success" };
|
||||
|
||||
export async function sendMiniAppNotification({
|
||||
fid,
|
||||
@@ -25,13 +25,13 @@ export async function sendMiniAppNotification({
|
||||
}): Promise<SendMiniAppNotificationResult> {
|
||||
const notificationDetails = await getUserNotificationDetails(fid);
|
||||
if (!notificationDetails) {
|
||||
return { state: 'no_token' };
|
||||
return { state: "no_token" };
|
||||
}
|
||||
|
||||
const response = await fetch(notificationDetails.url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
notificationId: crypto.randomUUID(),
|
||||
@@ -48,17 +48,17 @@ export async function sendMiniAppNotification({
|
||||
const responseBody = sendNotificationResponseSchema.safeParse(responseJson);
|
||||
if (responseBody.success === false) {
|
||||
// Malformed response
|
||||
return { state: 'error', error: responseBody.error.errors };
|
||||
return { state: "error", error: responseBody.error.errors };
|
||||
}
|
||||
|
||||
if (responseBody.data.result.rateLimitedTokens.length) {
|
||||
// Rate limited
|
||||
return { state: 'rate_limit' };
|
||||
return { state: "rate_limit" };
|
||||
}
|
||||
|
||||
return { state: 'success' };
|
||||
return { state: "success" };
|
||||
} else {
|
||||
// Error response
|
||||
return { state: 'error', error: responseJson };
|
||||
return { state: "error", error: responseJson };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const truncateAddress = (address: string) => {
|
||||
if (!address) return '';
|
||||
if (!address) return "";
|
||||
return `${address.slice(0, 14)}...${address.slice(-12)}`;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type Manifest } from '@farcaster/miniapp-node';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { type Manifest } from '@farcaster/miniapp-node';
|
||||
import {
|
||||
APP_BUTTON_TEXT,
|
||||
APP_DESCRIPTION,
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
APP_PRIMARY_CATEGORY,
|
||||
APP_SPLASH_BACKGROUND_COLOR,
|
||||
APP_SPLASH_URL,
|
||||
APP_TAGS,
|
||||
APP_URL,
|
||||
APP_TAGS, APP_URL,
|
||||
APP_WEBHOOK_URL,
|
||||
APP_ACCOUNT_ASSOCIATION,
|
||||
} from './constants';
|
||||
@@ -22,12 +21,12 @@ export function cn(...inputs: ClassValue[]) {
|
||||
|
||||
export function getMiniAppEmbedMetadata(ogImageUrl?: string) {
|
||||
return {
|
||||
version: 'next',
|
||||
version: "next",
|
||||
imageUrl: ogImageUrl ?? APP_OG_IMAGE_URL,
|
||||
button: {
|
||||
title: APP_BUTTON_TEXT,
|
||||
action: {
|
||||
type: 'launch_frame',
|
||||
type: "launch_frame",
|
||||
name: APP_NAME,
|
||||
url: APP_URL,
|
||||
splashImageUrl: APP_SPLASH_URL,
|
||||
@@ -45,12 +44,12 @@ export async function getFarcasterDomainManifest(): Promise<Manifest> {
|
||||
return {
|
||||
accountAssociation: APP_ACCOUNT_ASSOCIATION,
|
||||
miniapp: {
|
||||
version: '1',
|
||||
name: APP_NAME ?? 'Neynar Starter Kit',
|
||||
version: "1",
|
||||
name: APP_NAME ?? "Neynar Starter Kit",
|
||||
iconUrl: APP_ICON_URL,
|
||||
homeUrl: APP_URL,
|
||||
imageUrl: APP_OG_IMAGE_URL,
|
||||
buttonTitle: APP_BUTTON_TEXT ?? 'Launch Mini App',
|
||||
buttonTitle: APP_BUTTON_TEXT ?? "Launch Mini App",
|
||||
splashImageUrl: APP_SPLASH_URL,
|
||||
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||
webhookUrl: APP_WEBHOOK_URL,
|
||||
|
||||
Reference in New Issue
Block a user