Initial commit: Lightning Lottery - Bitcoin Lightning Network powered lottery
Features: - Lightning Network payments via LNbits integration - Provably fair draws using CSPRNG - Random ticket number generation - Automatic payouts with retry/redraw logic - Nostr authentication (NIP-07) - Multiple draw cycles (hourly, daily, weekly, monthly) - PostgreSQL and SQLite database support - Real-time countdown and payment animations - Swagger API documentation - Docker support Stack: - Backend: Node.js, TypeScript, Express - Frontend: Next.js, React, TailwindCSS, Redux - Payments: LNbits
This commit is contained in:
6
front_end/src/store/hooks.ts
Normal file
6
front_end/src/store/hooks.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
import type { RootState, AppDispatch } from './index';
|
||||
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
|
||||
16
front_end/src/store/index.ts
Normal file
16
front_end/src/store/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import userReducer from './userSlice';
|
||||
import jackpotReducer from './jackpotSlice';
|
||||
import purchaseReducer from './purchaseSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
user: userReducer,
|
||||
jackpot: jackpotReducer,
|
||||
purchase: purchaseReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
45
front_end/src/store/jackpotSlice.ts
Normal file
45
front_end/src/store/jackpotSlice.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface Cycle {
|
||||
id: string;
|
||||
cycle_type: string;
|
||||
scheduled_at: string;
|
||||
pot_total_sats: number;
|
||||
ticket_price_sats: number;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface JackpotState {
|
||||
cycle: Cycle | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: JackpotState = {
|
||||
cycle: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const jackpotSlice = createSlice({
|
||||
name: 'jackpot',
|
||||
initialState,
|
||||
reducers: {
|
||||
setCycle: (state, action: PayloadAction<Cycle>) => {
|
||||
state.cycle = action.payload;
|
||||
state.loading = false;
|
||||
state.error = null;
|
||||
},
|
||||
setLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
setError: (state, action: PayloadAction<string>) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setCycle, setLoading, setError } = jackpotSlice.actions;
|
||||
export default jackpotSlice.reducer;
|
||||
|
||||
45
front_end/src/store/purchaseSlice.ts
Normal file
45
front_end/src/store/purchaseSlice.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface Ticket {
|
||||
id: string;
|
||||
serial_number: number;
|
||||
is_winning_ticket: boolean;
|
||||
}
|
||||
|
||||
interface PurchaseState {
|
||||
ticket_purchase_id: string | null;
|
||||
invoice_status: string | null;
|
||||
tickets: Ticket[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: PurchaseState = {
|
||||
ticket_purchase_id: null,
|
||||
invoice_status: null,
|
||||
tickets: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const purchaseSlice = createSlice({
|
||||
name: 'purchase',
|
||||
initialState,
|
||||
reducers: {
|
||||
setPurchase: (state, action: PayloadAction<Partial<PurchaseState>>) => {
|
||||
return { ...state, ...action.payload, loading: false, error: null };
|
||||
},
|
||||
setLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
setError: (state, action: PayloadAction<string>) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
clearPurchase: () => initialState,
|
||||
},
|
||||
});
|
||||
|
||||
export const { setPurchase, setLoading, setError, clearPurchase } = purchaseSlice.actions;
|
||||
export default purchaseSlice.reducer;
|
||||
|
||||
32
front_end/src/store/userSlice.ts
Normal file
32
front_end/src/store/userSlice.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface UserState {
|
||||
authenticated: boolean;
|
||||
pubkey: string | null;
|
||||
lightning_address: string | null;
|
||||
token: string | null;
|
||||
displayName: string | null;
|
||||
}
|
||||
|
||||
const initialState: UserState = {
|
||||
authenticated: false,
|
||||
pubkey: null,
|
||||
lightning_address: null,
|
||||
token: null,
|
||||
displayName: null,
|
||||
};
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState,
|
||||
reducers: {
|
||||
setUser: (state, action: PayloadAction<Partial<UserState>>) => {
|
||||
return { ...state, ...action.payload, authenticated: true };
|
||||
},
|
||||
logout: () => initialState,
|
||||
},
|
||||
});
|
||||
|
||||
export const { setUser, logout } = userSlice.actions;
|
||||
export default userSlice.reducer;
|
||||
|
||||
Reference in New Issue
Block a user