diff --git a/packages/client/src/pages/example/example.tsx b/packages/client/src/pages/example/example.tsx index 22f1807..fa8ee17 100644 --- a/packages/client/src/pages/example/example.tsx +++ b/packages/client/src/pages/example/example.tsx @@ -1,12 +1,12 @@ import { unwrapResult } from "@reduxjs/toolkit"; import React, { useEffect, useState } from "react"; import { + Location, createPost, getPosts, setLocationFilter, } from "../../redux/slices/post-slice"; import { login, signup } from "../../redux/slices/user-slice"; -import { Location } from "../../redux/slices/location-slice"; import { useAppDispatch, useAppSelector } from "../../redux/store"; const ExamplePage = () => { diff --git a/packages/client/src/pages/home/action-group/post-dialog.tsx b/packages/client/src/pages/home/action-group/post-dialog.tsx index 4ac0241..5a9ccfb 100644 --- a/packages/client/src/pages/home/action-group/post-dialog.tsx +++ b/packages/client/src/pages/home/action-group/post-dialog.tsx @@ -59,7 +59,7 @@ const StyledContainer = styled.div` const PostDialog = ({ open, onClose }: PostDialogProps) => { const classes = useStyles(); - const location = useAppSelector((state) => state.location); + const location = useAppSelector((state) => state.post.locationFilter); const dispatch = useAppDispatch(); const [fields, setFields]: [PostDialogFields, any] = useState(DEFAULT_FIELDS); const { title, bodyText, tag, isAnonymous, userId } = fields; diff --git a/packages/client/src/pages/home/map.tsx b/packages/client/src/pages/home/map.tsx index 2991a47..0400b39 100644 --- a/packages/client/src/pages/home/map.tsx +++ b/packages/client/src/pages/home/map.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import { GoogleMap, LoadScript, @@ -7,7 +7,8 @@ import { } from "@react-google-maps/api"; import { CSSProperties } from "@material-ui/styles"; import { Libraries } from "@react-google-maps/api/dist/utils/make-load-script-url"; -import { Location, setLocation } from "../../redux/slices/location-slice"; +import { Location } from "../../redux/slices/post-slice"; +import { setLocationFilter } from "../../redux/slices/post-slice"; import REACT_APP_GOOGLE_API_KEY from "../../env"; import styled from "styled-components"; import { useAppDispatch, useAppSelector } from "../../redux/store"; @@ -58,7 +59,7 @@ const onAutoCompleteLoad = (autocomplete: any) => { const Map = () => { const dispatch = useAppDispatch(); - const location = useAppSelector((state) => state.location); + const location = useAppSelector((state) => state.post.locationFilter); const updateStore = async ( lat: number, lon: number, @@ -66,7 +67,7 @@ const Map = () => { ) => { try { const response = await dispatch( - setLocation({ + setLocationFilter({ name: newName !== undefined ? newName : name, lat: lat, lon: lon, @@ -84,9 +85,10 @@ const Map = () => { lat: location.lat, lng: location.lon, }); + const [name, setName] = React.useState(location.name); - if (initialLocation === undefined) { + useEffect(() => { navigator.geolocation.getCurrentPosition(function (position) { initialLocation = { lat: position.coords.latitude, @@ -99,7 +101,7 @@ const Map = () => { }); updateStore(initialLocation.lat, initialLocation.lon, location.name); }); - } + }, [dispatch]); const updateOnDrag = (e: any) => { setCtr({ lat: e.latLng.lat(), lng: e.latLng.lng() }); diff --git a/packages/client/src/pages/home/post-list/post-list.tsx b/packages/client/src/pages/home/post-list/post-list.tsx index 6dad0be..4dccfa3 100644 --- a/packages/client/src/pages/home/post-list/post-list.tsx +++ b/packages/client/src/pages/home/post-list/post-list.tsx @@ -2,7 +2,10 @@ import React, { useEffect } from "react"; import styled from "styled-components"; import { useAppDispatch, useAppSelector } from "../../../redux/store"; import PostListItem from "./post-list-item"; -import { getPosts, PostSortType } from "../../../redux/slices/post-slice"; +import { + getPostsByFilter, + PostSortType, +} from "../../../redux/slices/post-slice"; const StyledContainer = styled.div` display: flex; @@ -30,13 +33,16 @@ const PostList: React.FC = () => { default: console.log("damn wtf"); } - return result; }); + const postState = useAppSelector((state) => state.post); + + let location = useAppSelector((state) => state.post.locationFilter); + useEffect(() => { - dispatch(getPosts()); - }, [dispatch]); + dispatch(getPostsByFilter(postState)); + }, [location]); return ( diff --git a/packages/client/src/redux/slices/location-slice.ts b/packages/client/src/redux/slices/location-slice.ts deleted file mode 100644 index fd84886..0000000 --- a/packages/client/src/redux/slices/location-slice.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; - -export type Location = { - name?: string; - lat: number; - lon: number; -}; - -export const initialState: Location = { - name: undefined, - lat: 49.26, - lon: -123.22, -}; - -export const locationSlice = createSlice({ - name: "location", - initialState, - reducers: { - setLocation(state, action) { - state = action.payload; - }, - getLocation(state) { - return state; - }, - }, -}); - -export const { setLocation, getLocation } = locationSlice.actions; -export default locationSlice.reducer; diff --git a/packages/client/src/redux/slices/post-slice.ts b/packages/client/src/redux/slices/post-slice.ts index 488ef42..4c368a0 100644 --- a/packages/client/src/redux/slices/post-slice.ts +++ b/packages/client/src/redux/slices/post-slice.ts @@ -1,16 +1,36 @@ import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; import postService from "../../services/posts"; -import { Location } from "./location-slice"; import { UserState } from "./user-slice"; const prefix = "post"; +export type Location = { + name?: string; + lat: number; + lon: number; +}; + +export const initialLocation: Location = { + name: undefined, + lat: 49.26, + lon: -123.22, +}; + +type Comment = { + _id: string; + date: string; + numUpVotes: number; + numDownvotes: number; + userId: string; + username: string; +}; + export interface Post extends NewPost { _id: string; numUpvotes: number; numDownvotes: number; date: string; - comments: string[]; + comments: Comment[]; username: string; } @@ -27,7 +47,7 @@ export enum PostSortType { NEW = "new", } -type PostState = { +export type PostState = { posts: Post[]; sortType: PostSortType; locationFilter: Location; @@ -35,14 +55,12 @@ type PostState = { currentPostID?: string; }; +let locationFilter: Location = initialLocation; + const initialState: PostState = { posts: [], sortType: PostSortType.POPULAR, - locationFilter: { - name: "Vancouver", - lat: 49.26, - lon: -123.22, - }, + locationFilter: locationFilter, }; export const createPost = createAsyncThunk( @@ -63,7 +81,18 @@ export const getPosts = createAsyncThunk( async (_, { rejectWithValue }) => { try { const response = await postService.getAll(); + return response.data; + } catch (error) { + return rejectWithValue(error); + } + }, +); +export const getPostsByFilter = createAsyncThunk( + `${prefix}/getPostsByFilter`, + async (postState: PostState, { rejectWithValue }) => { + try { + const response = await postService.getPostsByFilter(postState); return response.data; } catch (error) { return rejectWithValue(error); @@ -142,6 +171,12 @@ export const postSlice = createSlice({ builder.addCase(getPosts.rejected, (state, action) => { return { ...initialState }; }); + builder.addCase(getPostsByFilter.fulfilled, (state, action) => { + state.posts = action.payload; + }); + builder.addCase(getPostsByFilter.rejected, (state, action) => { + return { ...initialState }; + }); builder.addCase(createPost.fulfilled, (state, action) => { state.posts.push(action.payload); }); diff --git a/packages/client/src/redux/store.ts b/packages/client/src/redux/store.ts index 3bfc70b..24e166b 100644 --- a/packages/client/src/redux/store.ts +++ b/packages/client/src/redux/store.ts @@ -2,7 +2,6 @@ import { configureStore } from "@reduxjs/toolkit"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import postReducer from "./slices/post-slice"; import userReducer from "./slices/user-slice"; -import locationReducer from "./slices/location-slice"; // TODO write instructions for setting up db.json @@ -10,7 +9,6 @@ const store = configureStore({ reducer: { user: userReducer, post: postReducer, - location: locationReducer }, }); diff --git a/packages/client/src/services/posts.ts b/packages/client/src/services/posts.ts index 507bca3..a9b96aa 100644 --- a/packages/client/src/services/posts.ts +++ b/packages/client/src/services/posts.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { NewPost, Post } from "../redux/slices/post-slice"; +import { NewPost, Post, PostState } from "../redux/slices/post-slice"; const baseUrl = "/api/posts"; @@ -21,6 +21,19 @@ const getAll = async () => { return response; }; +const getPostsByFilter = async (postState: PostState) => { + const response = await axios.get(`${baseUrl}`, { + params: { + posts: [], + sortType: postState.sortType, + locationFilter: postState.locationFilter, + tagFilter: postState.tagFilter, + currentPostID: postState.currentPostID, + }, + }); + return response; +}; + const getByID = async (id: string) => { const response = await axios.get(`${baseUrl}/${id}`); return response; @@ -39,6 +52,7 @@ const deleteByID = async (id: string) => { const postService = { create, getAll, + getPostsByFilter, getByID, update, deleteByID, diff --git a/packages/server/setup/datagen.js b/packages/server/setup/datagen.js index 957f705..cf1f7f5 100644 --- a/packages/server/setup/datagen.js +++ b/packages/server/setup/datagen.js @@ -201,7 +201,7 @@ class DataGenerator { const rawLocations = fs.readFileSync(this.locationPath); this.mockLocations = JSON.parse(rawLocations).map((location) => ({ type: "Point", - coordinates: [location.lat, location.lng], + coordinates: [location.lng, location.lat], })); const rawComments = fs.readFileSync(this.commentsPath); this.mockComments = JSON.parse(rawComments); diff --git a/packages/server/setup/mock-data/location_bank.json b/packages/server/setup/mock-data/location_bank.json index 4875c57..ee5fc78 100644 --- a/packages/server/setup/mock-data/location_bank.json +++ b/packages/server/setup/mock-data/location_bank.json @@ -270,5 +270,581 @@ { "lat": 49.25327768587862, "lng": -122.62090179443359 + }, + { + "lat": 49.25847654335906, + "lng": -123.02054768720704 + }, + { + "lat": 49.241220230365144, + "lng": -122.97831898847657 + }, + { + "lat": 49.235392061159004, + "lng": -123.15307027021485 + }, + { + "lat": 49.2185761811527, + "lng": -123.13693410078126 + }, + { + "lat": 49.20422210447345, + "lng": -123.1393373600586 + }, + { + "lat": 49.199959935629174, + "lng": -123.10191517988282 + }, + { + "lat": 49.1916588683966, + "lng": -123.07959920087892 + }, + { + "lat": 49.180438996632354, + "lng": -123.09745198408204 + }, + { + "lat": 49.172808030591675, + "lng": -123.12697774091798 + }, + { + "lat": 49.17011446757084, + "lng": -123.06277638593751 + }, + { + "lat": 49.163828917253284, + "lng": -123.09607869306642 + }, + { + "lat": 49.157093513596585, + "lng": -123.13281422773439 + }, + { + "lat": 49.16203289917644, + "lng": -123.17092305341798 + }, + { + "lat": 49.15215363539985, + "lng": -123.1448305241211 + }, + { + "lat": 49.146764115524896, + "lng": -123.07410603681642 + }, + { + "lat": 49.14766240955124, + "lng": -122.97351246992189 + }, + { + "lat": 49.14317077656162, + "lng": -122.89454823652345 + }, + { + "lat": 49.140026391215024, + "lng": -122.83240681806642 + }, + { + "lat": 49.15911423087464, + "lng": -122.79738789716798 + }, + { + "lat": 49.175950335557516, + "lng": -122.85472279707032 + }, + { + "lat": 49.188517560750114, + "lng": -122.85609608808595 + }, + { + "lat": 49.19322944742452, + "lng": -122.82416707197267 + }, + { + "lat": 49.198165228323056, + "lng": -122.79498463789064 + }, + { + "lat": 49.209156788428075, + "lng": -122.80700093427735 + }, + { + "lat": 49.22037014769664, + "lng": -122.7932680241211 + }, + { + "lat": 49.22799377903949, + "lng": -122.78056508222657 + }, + { + "lat": 49.238754550404046, + "lng": -122.79017811933595 + }, + { + "lat": 49.24054778440251, + "lng": -122.77301198164064 + }, + { + "lat": 49.256683960447646, + "lng": -122.81627064863282 + }, + { + "lat": 49.26250961687002, + "lng": -122.87635213056642 + }, + { + "lat": 49.276174803857536, + "lng": -122.8588426701172 + }, + { + "lat": 49.27751871615354, + "lng": -122.81798726240235 + }, + { + "lat": 49.278414637340795, + "lng": -122.77369862714845 + }, + { + "lat": 49.28378982269973, + "lng": -122.80562764326173 + }, + { + "lat": 49.28826869629172, + "lng": -122.7544725529297 + }, + { + "lat": 49.296329643515065, + "lng": -122.80082112470704 + }, + { + "lat": 49.296105746113376, + "lng": -122.83892995039064 + }, + { + "lat": 49.30058350090548, + "lng": -122.89145833173829 + }, + { + "lat": 49.30282222574407, + "lng": -122.95874959150392 + }, + { + "lat": 49.30438927261749, + "lng": -123.0349672428711 + }, + { + "lat": 49.316476244917084, + "lng": -123.07067280927735 + }, + { + "lat": 49.318490451986996, + "lng": -123.11942464033204 + }, + { + "lat": 49.33370624309262, + "lng": -123.15101033369142 + }, + { + "lat": 49.338627992321584, + "lng": -123.18431264082032 + }, + { + "lat": 49.3448913250081, + "lng": -123.14551716962892 + }, + { + "lat": 49.33907539966531, + "lng": -123.10328847089845 + }, + { + "lat": 49.33325878693996, + "lng": -123.0679262272461 + }, + { + "lat": 49.32878400169998, + "lng": -123.02260762373048 + }, + { + "lat": 49.32229484059632, + "lng": -122.98140889326173 + }, + { + "lat": 49.32475634720656, + "lng": -122.95531636396485 + }, + { + "lat": 49.31379050735365, + "lng": -122.90519124189454 + }, + { + "lat": 49.30035962282818, + "lng": -122.89969807783204 + }, + { + "lat": 49.28558142095883, + "lng": -122.94021016279298 + }, + { + "lat": 49.275950814915106, + "lng": -122.98930531660157 + }, + { + "lat": 49.268110561087504, + "lng": -123.04423695722657 + }, + { + "lat": 49.26206151386756, + "lng": -123.10122853437501 + }, + { + "lat": 49.278414637341356, + "lng": -123.11805134931642 + }, + { + "lat": 49.281774196848275, + "lng": -123.12766438642579 + }, + { + "lat": 49.28804476227536, + "lng": -123.13109761396485 + }, + { + "lat": 49.290060131812076, + "lng": -123.13830739179689 + }, + { + "lat": 49.29453843582412, + "lng": -123.13968068281251 + }, + { + "lat": 49.29834467436289, + "lng": -123.14174061933595 + }, + { + "lat": 49.28513352749721, + "lng": -123.1173647038086 + }, + { + "lat": 49.28513352749721, + "lng": -123.10672169843751 + }, + { + "lat": 49.28646482099149, + "lng": -123.13075429121095 + }, + { + "lat": 49.27280248479775, + "lng": -123.13830739179689 + }, + { + "lat": 49.262945335216706, + "lng": -123.15753346601564 + }, + { + "lat": 49.25891229731549, + "lng": -123.1997621647461 + }, + { + "lat": 49.254430757529605, + "lng": -123.23340779462892 + }, + { + "lat": 49.25622342227346, + "lng": -123.24645405927735 + }, + { + "lat": 49.26272128620223, + "lng": -123.25229054609376 + }, + { + "lat": 49.26608191460955, + "lng": -123.24130421796876 + }, + { + "lat": 49.25734380467666, + "lng": -123.24130421796876 + }, + { + "lat": 49.26518576941354, + "lng": -123.23443776289064 + }, + { + "lat": 49.2624972361705, + "lng": -123.24611073652345 + }, + { + "lat": 49.25599934274102, + "lng": -123.23958760419923 + }, + { + "lat": 49.26115291461765, + "lng": -123.22894459882814 + }, + { + "lat": 49.260480740108264, + "lng": -123.24164754072267 + }, + { + "lat": 49.24770768487859, + "lng": -123.19495564619142 + }, + { + "lat": 49.23425879275879, + "lng": -123.19358235517579 + }, + { + "lat": 49.2288782103868, + "lng": -123.15890675703126 + }, + { + "lat": 49.222600123597665, + "lng": -123.11942464033204 + }, + { + "lat": 49.20690141667763, + "lng": -123.11496144453126 + }, + { + "lat": 49.20622850353437, + "lng": -123.08577901044923 + }, + { + "lat": 49.20622850353437, + "lng": -123.04801350751954 + }, + { + "lat": 49.19299268391662, + "lng": -123.0840623966797 + }, + { + "lat": 49.18962708026325, + "lng": -123.04183369794923 + }, + { + "lat": 49.18581245265944, + "lng": -123.09573537031251 + }, + { + "lat": 49.18581245265944, + "lng": -123.1499803654297 + }, + { + "lat": 49.18985146096356, + "lng": -123.1829393498047 + }, + { + "lat": 49.1891783158098, + "lng": -123.18053609052735 + }, + { + "lat": 49.18289518604959, + "lng": -123.17057973066407 + }, + { + "lat": 49.18199753095364, + "lng": -123.07135945478517 + }, + { + "lat": 49.1826707738021, + "lng": -123.03428059736329 + }, + { + "lat": 49.17459125511507, + "lng": -123.06895619550782 + }, + { + "lat": 49.25848884374997, + "lng": -122.99479738066405 + }, + { + "lat": 49.26297001507769, + "lng": -123.06723848173827 + }, + { + "lat": 49.25400726551396, + "lng": -123.02226320097655 + }, + { + "lat": 49.25535178171073, + "lng": -123.08337465117187 + }, + { + "lat": 49.25759256065577, + "lng": -123.12697664091796 + }, + { + "lat": 49.2605054212018, + "lng": -123.11976686308593 + }, + { + "lat": 49.264986409423905, + "lng": -123.15890565703124 + }, + { + "lat": 49.26453832891212, + "lng": -123.1348730642578 + }, + { + "lat": 49.26857090703713, + "lng": -123.13761964628905 + }, + { + "lat": 49.26991502650551, + "lng": -123.12320009062499 + }, + { + "lat": 49.27685906039454, + "lng": -123.12663331816405 + }, + { + "lat": 49.27820295404641, + "lng": -123.10019746611327 + }, + { + "lat": 49.28066666397005, + "lng": -123.08440461943358 + }, + { + "lat": 49.28066666397005, + "lng": -123.05075898955077 + }, + { + "lat": 49.28335420715614, + "lng": -123.03324952910155 + }, + { + "lat": 49.28447397358849, + "lng": -123.00750032255858 + }, + { + "lat": 49.284250022336295, + "lng": -122.98758760283202 + }, + { + "lat": 49.281786491430964, + "lng": -122.96630159208983 + }, + { + "lat": 49.27999475528768, + "lng": -122.94501558134765 + }, + { + "lat": 49.27708304521276, + "lng": -122.92887941191405 + }, + { + "lat": 49.27529113818596, + "lng": -122.90828004667968 + }, + { + "lat": 49.273499166059565, + "lng": -122.8818441946289 + }, + { + "lat": 49.27282715972855, + "lng": -122.86605134794921 + }, + { + "lat": 49.27282715972855, + "lng": -122.83652559111327 + }, + { + "lat": 49.26946699075158, + "lng": -122.80150667021483 + }, + { + "lat": 49.26341810983096, + "lng": -122.85403505156249 + }, + { + "lat": 49.25086991863329, + "lng": -122.90004030058593 + }, + { + "lat": 49.236301053259965, + "lng": -122.93368593046874 + }, + { + "lat": 49.22621239767707, + "lng": -122.9590918142578 + }, + { + "lat": 49.22621239767707, + "lng": -122.93025270292968 + }, + { + "lat": 49.22979971125854, + "lng": -122.96870485136718 + }, + { + "lat": 49.225539747383614, + "lng": -122.9972006399414 + }, + { + "lat": 49.2167944602692, + "lng": -123.01230684111327 + }, + { + "lat": 49.20715042679016, + "lng": -123.05041566679687 + }, + { + "lat": 49.20221554258978, + "lng": -123.02603975126952 + }, + { + "lat": 49.19010055693165, + "lng": -123.05590883085937 + }, + { + "lat": 49.18269549378681, + "lng": -123.03393617460937 + }, + { + "lat": 49.171249125425575, + "lng": -123.0686117727539 + }, + { + "lat": 49.158003945758104, + "lng": -123.07135835478515 + }, + { + "lat": 49.15530957733596, + "lng": -123.04045930693358 + }, + { + "lat": 49.15216596226807, + "lng": -123.00715699980468 + }, + { + "lat": 49.14767473753695, + "lng": -122.97145143339843 + }, + { + "lat": 49.1452043903916, + "lng": -122.92269960234374 + }, + { + "lat": 49.15239051281735, + "lng": -122.8873373586914 + }, + { + "lat": 49.14947127628579, + "lng": -122.8547216970703 + }, + { + "lat": 49.15643224865679, + "lng": -122.83068910429687 + }, + { + "lat": 49.16384124121613, + "lng": -122.87360444853515 + }, + { + "lat": 49.171698052483976, + "lng": -122.89729371855468 + }, + { + "lat": 49.18314431703994, + "lng": -122.89523378203124 } ] diff --git a/packages/server/src/data/db/db-operations/post-ops.js b/packages/server/src/data/db/db-operations/post-ops.js index 4478cc6..daeda10 100644 --- a/packages/server/src/data/db/db-operations/post-ops.js +++ b/packages/server/src/data/db/db-operations/post-ops.js @@ -6,8 +6,24 @@ const getPosts = async () => { return await Post.aggregate([{ $sample: { size: 50 } }]); }; +const getPostsByFilter = async (lon, lat) => { + return await Post.find({ + location: { + $near: { + $geometry: { + type: "Point", + coordinates: [lon, lat], + }, + $minDistance: 0, + $maxDistance: 750, + }, + }, + }); +}; + const operations = { getPosts, + getPostsByFilter, }; module.exports = operations; diff --git a/packages/server/src/data/schemas/post-schema.js b/packages/server/src/data/schemas/post-schema.js index 7fa6019..cc82136 100644 --- a/packages/server/src/data/schemas/post-schema.js +++ b/packages/server/src/data/schemas/post-schema.js @@ -24,4 +24,6 @@ const PostSchema = new Schema({ downvoters: { type: [ObjectId], index: true }, }); +PostSchema.index({ location: "2dsphere" }); + module.exports = PostSchema; diff --git a/packages/server/src/routes/api/posts.js b/packages/server/src/routes/api/posts.js index 13d986d..ff4348f 100644 --- a/packages/server/src/routes/api/posts.js +++ b/packages/server/src/routes/api/posts.js @@ -3,8 +3,17 @@ const router = express.Router(); const operations = require("../../data/db/db-operations/post-ops"); router.get("/", async function (req, res, next) { + const locationFilter = JSON.parse(req.query.locationFilter); + let posts; try { - const posts = await operations.getPosts(); + if (req.query !== {}) { + posts = await operations.getPostsByFilter( + locationFilter.lon, + locationFilter.lat, + ); + } else { + posts = await operations.getPosts(); + } res.json(posts); } catch (err) { next(err);