Initial commit

This commit is contained in:
payacom
2026-07-07 18:21:47 +03:30
parent 41b380452f
commit 2b96445a54
69 changed files with 1007 additions and 553 deletions

View File

@@ -57,6 +57,10 @@ axiosInstance.interceptors.response.use(
if (status === 401 && !noToast) {
handleUnauthorized();
} else if (status === 403 && (data as { type?: string })?.type === "block") {
if (!noToast && (data as { message?: string })?.message) {
toast.error((data as { message: string }).message);
}
} else if (status === 422) {
const errors = (data as { [key: string]: string[] }).errors || {};
if (!noToast) {
@@ -73,6 +77,13 @@ axiosInstance.interceptors.response.use(
});
}
}
} else if (status === 404 && !noToast) {
if (
typeof data === "object" &&
(data as { message?: string }).message
) {
toast.error((data as { message: string }).message);
}
} else if ([429, 500, 503].includes(status)) {
if (!noToast) {
// toast.error("An unexpected error occurred. Please try again later.");

66
src/hooks/useLongPress.ts Normal file
View File

@@ -0,0 +1,66 @@
"use client";
import { useCallback, useRef, useState } from "react";
type LongPressOptions = {
delay?: number;
};
export function useLongPress(
onLongPress: () => void,
{ delay = 2000 }: LongPressOptions = {}
) {
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const blockClickRef = useRef(false);
const [pressing, setPressing] = useState(false);
const clear = useCallback(() => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
}, []);
const onPointerDown = useCallback(
(e: React.PointerEvent) => {
if (e.button !== 0) return;
blockClickRef.current = false;
setPressing(true);
clear();
timerRef.current = setTimeout(() => {
blockClickRef.current = true;
setPressing(false);
if (typeof navigator !== "undefined" && navigator.vibrate) {
navigator.vibrate(15);
}
onLongPress();
}, delay);
},
[clear, delay, onLongPress]
);
const endPress = useCallback(() => {
clear();
setPressing(false);
}, [clear]);
const shouldBlockClick = useCallback(() => {
if (blockClickRef.current) {
blockClickRef.current = false;
return true;
}
return false;
}, []);
return {
pressing,
shouldBlockClick,
handlers: {
onPointerDown,
onPointerUp: endPress,
onPointerLeave: endPress,
onPointerCancel: endPress,
onContextMenu: (e: React.MouseEvent) => e.preventDefault(),
},
};
}

23
src/hooks/usePageTitle.ts Normal file
View File

@@ -0,0 +1,23 @@
"use client";
import { useEffect } from "react";
import { defaultSEOConfig } from "@/config/seoConfig";
const DEFAULT_TITLE =
typeof defaultSEOConfig.title === "string"
? defaultSEOConfig.title
: "مدستاگرام";
export function formatPageTitle(title: string) {
const trimmed = title.trim();
if (!trimmed) return DEFAULT_TITLE;
if (trimmed.includes("مدستاگرام")) return trimmed;
return `${trimmed} | مدستاگرام`;
}
export function usePageTitle(title?: string | null) {
useEffect(() => {
if (!title?.trim()) return;
document.title = formatPageTitle(title);
}, [title]);
}