[#1] Remove office phone and add email app
All checks were successful
Publish Docker Image / Publish Docker Image (push) Successful in 1m1s

This commit is contained in:
2025-05-01 22:32:48 -04:00
parent 257d56c327
commit 84b1c36073
6 changed files with 31 additions and 194 deletions

View File

@@ -15,7 +15,6 @@ import {
BackSide,
} from "three";
import { Billboard, Text } from "@react-three/drei";
import { useConfig } from "../hooks/useConfig";
interface ButtonsProps {
menuTraversal: Stack<View>;
@@ -23,7 +22,6 @@ interface ButtonsProps {
pendingView: View | null;
setPendingView: Dispatch<SetStateAction<View | null>>;
goPreviousView(): void;
phoneHovered: boolean;
currentView: View;
}
@@ -35,11 +33,8 @@ export const Buttons = ({
pendingView,
setPendingView,
goPreviousView,
phoneHovered,
currentView,
}: ButtonsProps) => {
const config = useConfig();
const [hovered, setHovered] = useState<View | null>(null);
const desktopRef = useDisableRaycast(currentView !== View.MainView);
@@ -47,8 +42,6 @@ export const Buttons = ({
const sideRef = useDisableRaycast(currentView !== View.MainView);
const cellphoneRef = useDisableRaycast(currentView !== View.DesktopView);
const pcRef = useDisableRaycast(currentView !== View.DesktopView);
const resumeRef = useDisableRaycast(currentView !== View.SideView);
const phoneRef = useDisableRaycast(currentView !== View.SideView);
const goToView = useCallback(
(view: View) => {
@@ -89,12 +82,12 @@ export const Buttons = ({
}, [menuTraversal, pendingView, setPendingView]);
useEffect(() => {
if (hovered || phoneHovered) {
if (hovered) {
document.body.style.cursor = "pointer";
} else {
document.body.style.cursor = "auto";
}
}, [hovered, phoneHovered]);
}, [hovered]);
const mainMenuSpring = useThreeSpring<MeshStandardMaterial>({
opacity: currentView === View.MainView ? 1 : 0,
@@ -116,14 +109,11 @@ export const Buttons = ({
const printerMenuSpring = useThreeSpring<MeshStandardMaterial>({
opacity: currentView === View.PrinterView ? 1 : 0,
});
const sideMenuSpring = useThreeSpring<MeshStandardMaterial>({
opacity: currentView === View.SideView ? 1 : 0,
});
const desktopViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.DesktopView ? 1.25 : 1,
});
const sideViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.SideView ? 1.25 : 1,
const resumeViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.ResumeView ? 1.25 : 1,
});
const cellphoneViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.CellphoneView ? 1.25 : 1,
@@ -134,15 +124,9 @@ export const Buttons = ({
const creditsViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.CreditsView ? 1.25 : 1,
});
const resumeViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.ResumeView ? 1.25 : 1,
});
const printerViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.PrinterView ? 1.25 : 1,
});
const phoneViewSpring = useThreeSpring<BufferGeometry>({
scale: hovered === View.PhoneView ? 1.25 : 1,
});
return (
<>
@@ -242,16 +226,16 @@ export const Buttons = ({
position={[2.525, 6, 6.75]}
onClick={
currentView === View.MainView
? () => goToView(View.SideView)
? () => goToView(View.ResumeView)
: undefined
}
onPointerOver={
currentView === View.MainView
? () => setHovered(View.SideView)
? () => setHovered(View.ResumeView)
: undefined
}
onPointerOut={() => setHovered(null)}
scale={sideViewSpring.scale}
scale={resumeViewSpring.scale}
>
<sphereGeometry args={[0.6, 32, 32]} />
<animated.meshStandardMaterial
@@ -260,7 +244,10 @@ export const Buttons = ({
opacity={mainMenuSpring.opacity}
/>
</animated.mesh>
<animated.mesh position={[2.525, 6, 6.75]} scale={sideViewSpring.scale}>
<animated.mesh
position={[2.525, 6, 6.75]}
scale={resumeViewSpring.scale}
>
<sphereGeometry args={[0.65, 32, 32]} />
<animated.meshStandardMaterial
transparent
@@ -277,7 +264,7 @@ export const Buttons = ({
fillOpacity={mainMenuSpring.opacity}
outlineOpacity={mainMenuSpring.opacity}
>
Resume & Contact
Resume
</AnimatedText>
</Billboard>
</group>
@@ -450,105 +437,6 @@ export const Buttons = ({
</AnimatedText>
</Billboard>
</group>
{/* Side Menu */}
<group>
<animated.mesh
ref={resumeRef}
position={[2.845, 4.75, 6.454]}
onClick={
currentView === View.SideView
? () => goToView(View.ResumeView)
: undefined
}
onPointerOver={
currentView === View.SideView
? () => setHovered(View.ResumeView)
: undefined
}
onPointerOut={() => setHovered(null)}
scale={resumeViewSpring.scale}
>
<sphereGeometry args={[0.175, 32, 32]} />
<animated.meshStandardMaterial
transparent
color="white"
opacity={sideMenuSpring.opacity}
/>
</animated.mesh>
<animated.mesh
position={[2.845, 4.75, 6.454]}
scale={resumeViewSpring.scale}
>
<sphereGeometry args={[0.175 + 7 / 480, 32, 32]} />
<animated.meshStandardMaterial
transparent
color="black"
opacity={sideMenuSpring.opacity}
side={BackSide}
/>
</animated.mesh>
<Billboard position={[2.845, 5.1875, 6.454]}>
<AnimatedText
font="/assets/inter.ttf"
fontSize={0.21875}
outlineWidth={7 / 480}
fillOpacity={sideMenuSpring.opacity}
outlineOpacity={sideMenuSpring.opacity}
>
Resume
</AnimatedText>
</Billboard>
{config.phoneNumber && (
<>
<animated.mesh
ref={phoneRef}
position={[3.75, 5.125, 7]}
onClick={
currentView === View.SideView
? () => goToView(View.PhoneView)
: undefined
}
onPointerOver={
currentView === View.SideView
? () => setHovered(View.PhoneView)
: undefined
}
onPointerOut={() => setHovered(null)}
scale={phoneViewSpring.scale}
>
<sphereGeometry args={[0.175, 32, 32]} />
<animated.meshStandardMaterial
transparent
color="white"
opacity={sideMenuSpring.opacity}
/>
</animated.mesh>
<animated.mesh
position={[3.75, 5.125, 7]}
scale={phoneViewSpring.scale}
>
<sphereGeometry args={[0.175 + 7 / 480, 32, 32]} />
<animated.meshStandardMaterial
transparent
color="black"
opacity={sideMenuSpring.opacity}
side={BackSide}
/>
</animated.mesh>
<Billboard position={[3.75, 5.515, 7]}>
<AnimatedText
font="/assets/inter.ttf"
fontSize={0.21875}
outlineWidth={7 / 480}
fillOpacity={sideMenuSpring.opacity}
outlineOpacity={sideMenuSpring.opacity}
>
Contact
</AnimatedText>
</Billboard>
</>
)}
</group>
</>
);
};

View File

@@ -56,7 +56,6 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
// States
const [pendingView, setPendingView] = useState<View | null>(null);
const [phoneHovered, setPhoneHovered] = useState(false);
const [backHovered, setBackHovered] = useState(false);
const [backClicked, setBackClicked] = useState(false);
const [menuTraversal, setMenuTraversal] = useState<Stack<View>>(() => {
@@ -182,7 +181,6 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
pendingView={pendingView}
setPendingView={setPendingView}
goPreviousView={goPreviousView}
phoneHovered={phoneHovered}
currentView={currentView}
/>
<group>
@@ -321,67 +319,6 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
</Html>
</mesh>
</group>
<group
position={[4, 4.74056, 7.25]}
rotation={[-Math.PI, -Math.PI / 4, -Math.PI]}
scale={0.3}
>
<group
position={[-0.45957, -1.57735, -0.97302]}
rotation={[-Math.PI / 2, 0, 0]}
scale={100}
onClick={
config.phoneNumber && currentView === View.PhoneView
? () => {
window.open(
`tel:${config.phoneNumber}`,
"_blank",
"noopener,noreferrer"
);
}
: undefined
}
onPointerOver={
config.phoneNumber && currentView === View.PhoneView
? () => setPhoneHovered(true)
: undefined
}
onPointerOut={() => setPhoneHovered(false)}
>
<mesh
castShadow
receiveShadow
geometry={nodes.Top.geometry}
material={materials.TopMaterial}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.OfficePhoneBody.geometry}
material={materials.OfficePhoneBodyMaterial}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Screen.geometry}
material={materials.ScreenMaterial}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Buttons.geometry}
material={materials.HandleAndButtonsMaterial}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Handle.geometry}
material={materials.HandleAndButtonsMaterial}
position={[-0.01639, -0.0078, 0.02006]}
rotation={[-0.37437, 0, Math.PI / 2]}
/>
</group>
</group>
<Text
position={[-2.415, 12.5, -6]}
font="/assets/inter-bold.ttf"

View File

@@ -36,11 +36,6 @@ export const views: PerspectiveCameraProps[] = [
position: [3.0143, 5.5, 6.0997],
rotation: [-2.55743, -0.69621, -2.74104],
},
{
// SideView
position: [0.5, 7.75, 4.15],
rotation: [-2.1005, -0.68155, -2.35619],
},
{
// CreditsView
position: [1.25, 5.9822, -2.075],
@@ -56,8 +51,7 @@ export enum View {
CellphoneView = 4,
DesktopView = 5,
PhoneView = 6,
SideView = 7,
CreditsView = 8,
CreditsView = 7,
}
export const hashtoView: Record<string, View> = {

View File

@@ -27,6 +27,7 @@ import {
mdiWifi,
mdiBattery80,
mdiHelp,
mdiEmail,
} from "@mdi/js";
import Icon from "@mdi/react";
import { useRef, useState } from "react";
@@ -88,6 +89,17 @@ export const CellphoneUI = () => {
<div className={`${appClass} ${fillerClass}`}>
<Icon path={mdiCalculator} className="text-neutral-300" />
</div>
{config.email && (
<a
href={`mailto:${config.email}`}
target="_blank"
className={appClass}
onMouseOver={handleEnter}
onMouseOut={handleLeave}
>
<Icon path={mdiEmail} className="text-neutral-300" />
</a>
)}
{config.socials?.matrix && (
<a
href={`https://matrix.to/#/${config.socials.matrix}`}

View File

@@ -66,6 +66,11 @@ export async function register() {
format: "phone",
nullable: true,
},
email: {
type: "string",
format: "email",
nullable: true,
},
fallbackUrl: {
type: "string",
format: "uri",

View File

@@ -16,6 +16,7 @@ export interface Config {
tags?: string[];
}[];
phoneNumber?: string;
email?: string;
fallbackUrl?: string;
name: [string, string];
status?: string;