Initial commit
This commit is contained in:
@@ -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
66
src/hooks/useLongPress.ts
Normal 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
23
src/hooks/usePageTitle.ts
Normal 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]);
|
||||
}
|
||||
Reference in New Issue
Block a user