Fintech Mobile App
A secure and intuitive mobile banking application built for a modern fintech startup.
Security First
Security was the top priority. We implemented:
- Biometric authentication (FaceID/TouchID)
- End-to-end encryption for all transactions
- Automatic session timeouts
User Experience
We focused on making complex financial tasks simple. Sending money is as easy as sending a text message.
Tech Stack
- Framework: React Native
- State Management: Redux Toolkit
- Backend: Firebase Functions & Firestore
TEST TEST!
"use client";
import { useEffect, useRef, useState } from "react";
import videojs from "video.js";
import type Player from "video.js/dist/types/player";
import "video.js/dist/video-js.css";
type Props = {
src: string;
title?: string;
isActive: boolean;
};
const VideoPlayer = ({ src, title, isActive }: Props) => {
const videoRef = useRef<HTMLDivElement | null>(null);
const playerRef = useRef<Player | null>(null);
const titleRef = useRef<string>(title ?? "Stream");
const [isError, setIsError] = useState(false);
useEffect(() => {
// Reset error state when src changes
setIsError(false);
if (!playerRef.current && videoRef.current) {
const videoElement = document.createElement("video");
videoElement.className = "video-js vjs-big-play-centered";
videoElement.setAttribute("playsinline", "");
videoRef.current.appendChild(videoElement);
const player = videojs(
videoElement,
{
autoplay: true,
controls: true,
responsive: true,
fluid: true,
sources: [
{
src: src,
type: "application/x-mpegURL",
},
],
},
() => {},
);
player.on("error", () => {
setIsError(true);
});
playerRef.current = player;
} else {
const player = playerRef.current;
if (player) {
player.src({ src: src, type: "application/x-mpegURL" });
}
}
// *** CRITICAL CLEANUP LOGIC ***
return () => {
const player = playerRef.current;
if (player && !player.isDisposed()) {
console.log(
`[${titleRef.current}] Unmounting: Disposing video.js player.`,
);
player.dispose();
playerRef.current = null;
} else {
console.log(
`[${titleRef.current}] Unmounting: Player already disposed or not found.`,
);
}
};
}, [src]);
// Separate effect to respond to isActive changes (play/pause) without reinitializing the player.
useEffect(() => {
const player = playerRef.current;
if (!player) return;
if (isActive) {
const playPromise = player.play?.();
if (playPromise && typeof playPromise.then === "function") {
playPromise.catch(() => {
try {
player.autoplay(true);
} catch {}
});
}
} else {
try {
player.pause();
} catch {}
}
}, [isActive]);
return (
//...
);
};
export default VideoPlayer;
