Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
"main": "server.js",
"scripts": {
"start": "npx nodemon server.js",
"dev": "nodemon server.js",
"worker:cluster": "node workers/eventClusteringWorker.js"
"start:api": "cross-env APP_ROLE=api node server.js",
"start:cluster-worker": "cross-env APP_ROLE=event-clustering-worker node server.js",
"start:update-worker": "cross-env APP_ROLE=photo-update-worker node server.js",
"start:sweeper": "cross-env APP_ROLE=sweeper node server.js",
"dev": "nodemon server.js"
},
"keywords": [],
"author": "",
Expand Down
92 changes: 66 additions & 26 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,80 @@
import dotenv from "dotenv";
dotenv.config();

import path from "path";
import { fileURLToPath } from "url";
import express from "express";
import cors from "cors";

import connectDB from "./config/db.js";
import router from "./v1.js";

const app = express();
import { startEventClusteringWorker } from "./workers/eventClusteringWorker.js";
import { startPhotoUpdateWorker } from "./workers/photoUpdateWorker.js";
import { startEventSweeper } from "./jobs/eventSweeper.js";

app.use(
cors({
origin: true,
credentials: true,
})
);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

app.use(express.json());
dotenv.config({ path: path.join(__dirname, ".env") });

app.use("/api/v1", router);
const APP_ROLE = process.env.APP_ROLE || "api";
const PORT = process.env.PORT || 5000;

app.get("/", (req, res) => {
res.send("Welcome to SnapMap API");
});
//API SERVER STARTUP

const PORT = process.env.PORT || 5000;
async function startApiServer() {
const app = express();

app.use(
cors({
origin: true,
credentials: true,
})
);

app.use(express.json());

app.use("/api/v1", router);

app.get("/", (req, res) => {
res.send("Welcome to SnapMap API");
});

const startServer = async () => {
try {
await connectDB();
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on port ${PORT}`);
});
} catch (error) {
console.error("Failed to start server:", error);
process.exit(1);
await connectDB();

app.listen(PORT, "0.0.0.0", () => {
console.log(`[api] Server running on port ${PORT}`);
});
}

//ROLE BASED BOOTSTRAP

async function bootstrap() {
console.log(`[bootstrap] Starting backend with role: ${APP_ROLE}`);

switch (APP_ROLE) {
case "api":
await startApiServer();
break;

case "event-clustering-worker":
await startEventClusteringWorker();
break;

case "photo-update-worker":
await startPhotoUpdateWorker();
break;

case "sweeper":
await startEventSweeper();
break;

default:
console.error(`[bootstrap] Invalid APP_ROLE: ${APP_ROLE}`);
process.exit(1);
}
};
}

startServer();
bootstrap().catch((err) => {
console.error("[bootstrap] Fatal startup error:", err);
process.exit(1);
});
6 changes: 6 additions & 0 deletions contributors/Outside_IIITA/2201660300059.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Name: Utkarsh
GitHub Username: utkarshp579
Favorite Country you want to Go: India
Favorite (Underrated) Movie/Anime/Series: Sita Ramam

Write some thing that you expect from this event: I will apply my existing knowledge in contributing, and gain expeirence in world of Open Source.
Binary file added frontend/src/assets/images/multi-photo-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/src/assets/images/single-photo-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions frontend/src/components/Toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useEffect, useRef } from "react";
import {
View,
Text,
StyleSheet,
Animated,
Dimensions,
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { Ionicons } from "@expo/vector-icons";

const { width } = Dimensions.get("window");

interface ToastProps {
message: string;
visible: boolean;
onHide: () => void;
success: boolean;
}

const Toast: React.FC<ToastProps> = ({ message, visible, onHide, success }) => {
const translateY = useRef(new Animated.Value(100)).current;
const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
if (visible) {
Animated.parallel([
Animated.timing(translateY, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(opacity, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}),
]).start();

const timer = setTimeout(() => {
hideToast();
}, 2500);

return () => clearTimeout(timer);
}
}, [visible]);

const hideToast = () => {
Animated.parallel([
Animated.timing(translateY, {
toValue: 100,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(opacity, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
]).start(onHide);
};

if (!visible) return null;

return (
<Animated.View
style={[
styles.container,
{ transform: [{ translateY }], opacity },
]}
>
<LinearGradient
colors={["#EC4899", "#F472B6"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.toast}
>
<Ionicons
name={success ? "checkmark-circle" : "close-circle"}
size={22}
color={success ? "#22C55E" : "#EF4444"}
/>

<Text style={styles.text}>{message}</Text>
</LinearGradient>
</Animated.View>
);
};

export default Toast;

const styles = StyleSheet.create({
container: {
position: "absolute",
bottom: 12,
width,
alignItems: "center",
zIndex: 1000,
},
toast: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 16,
width: width - 32,
gap: 10,
elevation: 6,
},
text: {
color: "#FFFFFF",
fontSize: 15,
fontWeight: "500",
},
});
4 changes: 2 additions & 2 deletions frontend/src/navigation/BottomNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ const BottomNavigation = () => {
icon: (
<MaterialCommunityIcons
name="compass-outline"
size={26}
color={isActive("MapScreen") ? "#EF4444" : "#9CA3AF"}
size={26} // Compass icon
color={isActive("MapScreen") || route.name === "SnapScreen" ? "#EF4444" : "#9CA3AF"}
/>
),
onPress: () => navigation.navigate("MapScreen"),
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/screens/MapScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const MapScreen = ({ navigation }: ScreenProps<"MapScreen">) => {
const [location, setLocation] = useState<Coordinates | null>(null);
const [photos, setPhotos] = useState<PhotoMarker[]>([]);

const singlePhoto = require("../assets/images/single-photo-icon.png");
const multiPhoto = require("../assets/images/multi-photo-icon.png");

const { profile } = useProfile();
const { user } = useUser();

Expand Down Expand Up @@ -149,12 +152,13 @@ const MapScreen = ({ navigation }: ScreenProps<"MapScreen">) => {
latitude: photo.latitude,
longitude: photo.longitude,
}}
image={require("../assets/images/b.png")}
image={photo.imageURL.length === 1 ? singlePhoto : multiPhoto}
onPress={() => navigation.navigate("SnapScreen", {
imageURL: photo.imageURL,
caption: photo.caption
caption: photo.caption,
latitude: photo.latitude,
longitude: photo.longitude,
})}


/>
</View>
Expand Down
Loading