Skip to content
12 changes: 5 additions & 7 deletions app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ import { StyleSheet } from "react-native";

import EditScreenInfo from "../../components/EditScreenInfo";
import { Text, View } from "../../components/Themed";
import PromptCard from "../../components/PromptCard/PromptCard";
import PromptResponseButton from "../../components/PromptResponseButton";

export default function TabOneScreen() {
return (
<View style={styles.container}>
<Text className="text-xl font-bold text-blue-700">Tab One</Text>
<View
style={styles.separator}
lightColor="#eee"
darkColor="rgba(255,255,255,0.1)"
/>
<EditScreenInfo path="app/(tabs)/index.tsx" />
<PromptCard prompt={"What is the most interesting thing you've learned this week?"} tag={"Text"} startTime={"2024-10-18T00:00:00"} duration={86400} numAnswers={3}/>
<PromptResponseButton tag = "Text" onPress={() => {console.log("Button Pressed")}}/>
</View>
);
}
Expand All @@ -22,6 +19,7 @@ const styles = StyleSheet.create({
flex: 1,
alignItems: "center",
justifyContent: "center",
gap: 32
},
title: {
fontSize: 20,
Expand Down
7 changes: 7 additions & 0 deletions assets/images/file-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions assets/images/image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions assets/images/image_tag_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/images/message-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/images/text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions assets/images/text_tag_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions assets/images/video_tag_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions components/PromptCard/PromptCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from "react";
import { Text, View } from "react-native";
import PromptCardCountdown from "./PromptCardCountdown";
import { LinearGradient } from "expo-linear-gradient";
import { Image } from "expo-image";

type PromptCardProps = {
prompt: string;
tag: string;
startTime: string; //ISO Date-Time format eg. 2024-04-22T15:33:27
duration: number; //Duration in seconds
numAnswers: number;
};

// Map of tag to image source
const tagImageMap: { [key: string]: any } = {
Image: require("../../assets/images/image.svg"),
Text: require("../../assets/images/text.svg"),
// Video: require('../../assets/images/video.svg'),
};

// Component for prompt card, takes in prompt, tag, start time, duration and number of answers
export default function PromptCard({
prompt,
tag,
startTime,
duration,
numAnswers,
}: PromptCardProps) {
const imageSource = tagImageMap[tag];
return (
<View className="relative w-[327px] h-[248px] rounded-[20px] py-6">
{/* Linear gradient background */}
<LinearGradient
colors={["#D372E5", "#5731D6"]}
start={{ x: 0, y: 1 }}
end={{ x: 1, y: 0 }}
style={{
borderRadius: 20,
width: 327,
height: 248,
position: "absolute",
}}
/>
<View className="flex flex-col gap-y-4 p-6">
<PromptCardCountdown startTime={startTime} duration={duration} />
<Text className="text-white font-semibold text-2xl h-[100px]">
{prompt}
</Text>
<AnswersLabel numAnswers={numAnswers} />
</View>
{/* Tag Icon in background */}
<Image
source={imageSource}
style={{ width: 128, height: 128, position: "absolute" }}
className="-right-5 top-[70px]"
/>
</View>
);
}

type AnswersLabelProps = {
numAnswers: number;
};

function AnswersLabel({ numAnswers }: AnswersLabelProps) {
return (
<View className="relative flex-row items-center mt-4 gap-x-1">
<Image
source={require("../../assets/images/message-square.svg")}
style={{ width: 16, height: 16 }}
/>
<Text className=" text-white text-[14px] font-medium">
{numAnswers} answers
</Text>
</View>
);
}
88 changes: 88 additions & 0 deletions components/PromptCard/PromptCardCountdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useState, useEffect, useRef, useCallback } from "react";
import { Text, View, AppState } from "react-native";

type PromptCardCountdownProps = {
startTime: string; //ISO Date-Time format eg. 2024-04-22T15:33:27
duration: number; //Duration in seconds
};

export default function PromptCardCountdown({
startTime,
duration,
}: PromptCardCountdownProps) {
const [timeLeft, setTimeLeft] = useState<number>(0);
const intervalRef = useRef<NodeJS.Timeout | null>(null);

const parseStartTime = useCallback((time: string): number => {
return new Date(time).getTime();
}, []);

const calculateRemainingTime = useCallback(
(parsedStartTime: number): number => {
const currentTime = Date.now();
const elapsedTime = (currentTime - parsedStartTime) / 1000;
return Math.max(duration - elapsedTime, 0);
},
[duration]
);

const updateRemainingTime = useCallback(() => {
const parsedStartTime = parseStartTime(startTime);
const timeLeft = calculateRemainingTime(parsedStartTime);
setTimeLeft(timeLeft);
}, [parseStartTime, calculateRemainingTime, startTime]);

useEffect(() => {
updateRemainingTime();

if (intervalRef.current) {
clearInterval(intervalRef.current);
}

intervalRef.current = setInterval(() => {
updateRemainingTime();
}, 1000);

const handleAppStateChange = (nextAppState: string) => {
if (nextAppState === "active") {
updateRemainingTime();
}
};

const subscription = AppState.addEventListener(
"change",
handleAppStateChange
);

return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
subscription.remove();
};
}, [updateRemainingTime]);

const formatTime = (seconds: number): string => {
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(
2,
"0"
)}:${String(secs).padStart(2, "0")}`;
};

return (
<View
className="flex-row items-center justify-center px-4 py-2 rounded-[30px] gap-x-2 w-2/3 ml-[0.25px]"
style={{
backgroundColor: "rgba(255, 255, 255, 0.2)", // 20% opacity
}}
>
<Text className="text-[14px] text-white font-medium">Expiring in</Text>
<Text className="text-[16px] text-white font-medium">
{formatTime(timeLeft)}
</Text>
</View>
);
}
25 changes: 25 additions & 0 deletions components/PromptResponseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Text, Pressable} from 'react-native';
import { Image } from 'expo-image'

type PromptResponseButtonProps = {
tag: string;
onPress: () => void;
}

// Map of tag to image source
const tagImageMap: { [key: string]: any } = {
Image: require("../assets/images/image_tag_icon.svg"),
Text: require("../assets/images/file-text.svg"),
Video: require('../assets/images/video_tag_icon.svg'),
};

export default function PromptCardButton({tag, onPress}: PromptResponseButtonProps) {
const imageSource = tagImageMap[tag];
return (
<Pressable className = "flex flex-row justify-center items-center bg-[#020205] h-[57px] rounded-[20px] w-[327px] gap-x-3" onPress={onPress}>
<Image source={imageSource} style={{width: 24, height: 24}}/>
<Text className = "text-[20px] text-white font-medium">Respond to the prompt</Text>
</Pressable>
);
}
1 change: 1 addition & 0 deletions nativewind.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="nativewind/types" />
Loading