diff --git a/src/components/Post/LivePostStream.jsx b/src/components/Post/LivePostStream.jsx
new file mode 100644
index 00000000..820acc4b
--- /dev/null
+++ b/src/components/Post/LivePostStream.jsx
@@ -0,0 +1,99 @@
+import { useEffect, useState } from 'react'
+import { Grid } from '@material-ui/core'
+import { useQuery, useSubscription } from '@apollo/react-hooks'
+import PropTypes from 'prop-types'
+import PostCard from './PostCard'
+import { GET_TOP_POSTS } from '../../graphql/query'
+import { NEW_POST_SUBSCRIPTION } from '../../graphql/subscription'
+
+function LivePostStream({
+ paused,
+ searchKey,
+ startDateRange,
+ endDateRange,
+ friendsOnly,
+ enabled,
+}) {
+ const { data, loading } = useQuery(GET_TOP_POSTS, {
+ variables: {
+ limit: 10,
+ offset: 0,
+ searchKey,
+ startDateRange,
+ endDateRange,
+ friendsOnly,
+ },
+ fetchPolicy: 'network-only',
+ skip: !enabled,
+ })
+
+ const [posts, setPosts] = useState([])
+ const [queued, setQueued] = useState([])
+
+ useEffect(() => {
+ if (data && data.posts) {
+ setPosts(data.posts.entities)
+ }
+ }, [data])
+
+ useSubscription(NEW_POST_SUBSCRIPTION, {
+ onSubscriptionData: ({ subscriptionData }) => {
+ const newPost = subscriptionData.data && subscriptionData.data.post
+ if (!newPost) return
+ if (paused) {
+ setQueued((q) => [newPost, ...q])
+ } else {
+ setPosts((p) => [newPost, ...p])
+ }
+ },
+ })
+
+ useEffect(() => {
+ if (!paused && queued.length) {
+ setPosts((p) => [...queued, ...p])
+ setQueued([])
+ }
+ }, [paused, queued])
+
+ if (loading && posts.length === 0) {
+ return
Loading...
+ }
+
+ return (
+
+ {posts.map((post) => (
+
+
+
+ ))}
+
+ )
+}
+
+LivePostStream.propTypes = {
+ paused: PropTypes.bool,
+ searchKey: PropTypes.string,
+ startDateRange: PropTypes.string,
+ endDateRange: PropTypes.string,
+ friendsOnly: PropTypes.bool,
+ enabled: PropTypes.bool,
+}
+
+LivePostStream.defaultProps = {
+ paused: false,
+ searchKey: '',
+ startDateRange: '',
+ endDateRange: '',
+ friendsOnly: false,
+ enabled: true,
+}
+
+export default LivePostStream
diff --git a/src/graphql/subscription.jsx b/src/graphql/subscription.jsx
index eddd6b8b..12165e6a 100644
--- a/src/graphql/subscription.jsx
+++ b/src/graphql/subscription.jsx
@@ -37,3 +37,37 @@ export const NEW_NOTIFICATION_SUBSCRIPTION = gql`
}
}
`
+
+export const NEW_POST_SUBSCRIPTION = gql`
+ subscription post {
+ post {
+ _id
+ userId
+ title
+ text
+ upvotes
+ downvotes
+ bookmarkedBy
+ created
+ url
+ creator {
+ name
+ username
+ avatar
+ _id
+ }
+ votes {
+ _id
+ startWordIndex
+ endWordIndex
+ type
+ }
+ comments { _id }
+ quotes { _id }
+ messageRoom {
+ _id
+ messages { _id }
+ }
+ }
+ }
+`
diff --git a/src/mui-pro/Sidebar/Sidebar.jsx b/src/mui-pro/Sidebar/Sidebar.jsx
index e788ec00..e0ee554f 100644
--- a/src/mui-pro/Sidebar/Sidebar.jsx
+++ b/src/mui-pro/Sidebar/Sidebar.jsx
@@ -13,12 +13,14 @@ import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import IconButton from "@material-ui/core/IconButton";
import Button from "@material-ui/core/Button";
+import Dialog from "@material-ui/core/Dialog";
import MenuIcon from "@material-ui/icons/Menu";
import sidebarStyle from "assets/jss/material-dashboard-pro-react/components/sidebarStyle";
import { SET_SELECTED_PAGE } from "../../store/ui";
import ChatMenu from "../../components/Chat/ChatMenu";
import NotificationMenu from "../../components/Notifications/NotificationMenu";
import SettingsMenu from "../../components/Settings/SettingsMenu";
+import SubmitPost from "../../components/SubmitPost/SubmitPost";
// We've created this component so we can have a ref to the wrapper of the links that appears in our sidebar.
// This was necessary so that we could initialize PerfectScrollbar on the links.
@@ -48,6 +50,7 @@ class MenuSidebar extends React.Component {
openAvatar: false,
miniActive: true,
MessageDisplay: null,
+ postDialogOpen: false,
...this.getCollapseStates(props.routes)
};
}
@@ -274,6 +277,14 @@ class MenuSidebar extends React.Component {
{loggedIn ? (
<>
+
@@ -313,6 +324,9 @@ class MenuSidebar extends React.Component {
/>
+
>
);
}
diff --git a/src/routes.jsx b/src/routes.jsx
index 89e2b0f4..d2589459 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -7,6 +7,7 @@ import LogoutPage from './components/LogoutPage'
import HomeSvg from './assets/svg/Home'
import ProfileAvatar from './components/Profile/ProfileAvatar'
import NotificationMobileView from './components/Notifications/NotificationMobileView'
+import SearchIcon from '@material-ui/icons/Search'
import SearchPage from 'views/SearchPage'
const routes = [
@@ -22,7 +23,7 @@ const routes = [
path: 'search',
name: 'Search',
rtlName: 'التقويم',
- icon: () =>
,
+ icon: SearchIcon,
component: SearchPage,
layout: '/',
},
diff --git a/src/views/SearchView/SearchView.jsx b/src/views/SearchView/SearchView.jsx
index f9a33014..3a17c373 100644
--- a/src/views/SearchView/SearchView.jsx
+++ b/src/views/SearchView/SearchView.jsx
@@ -1,9 +1,233 @@
-import React, { PureComponent } from 'react'
+import { useState, useEffect } from 'react'
+import { useSelector } from 'react-redux'
+import { makeStyles } from '@material-ui/core/styles'
+import {
+ IconButton,
+ Typography,
+ Button,
+ InputBase,
+ Paper,
+} from '@material-ui/core'
+import SearchIcon from '@material-ui/icons/Search'
+import DatePicker from 'react-datepicker'
+import 'react-datepicker/dist/react-datepicker.css'
+import format from 'date-fns/format'
+import PauseIcon from '@material-ui/icons/Pause'
+import PlayArrowIcon from '@material-ui/icons/PlayArrow'
+import LivePostStream from '../../components/Post/LivePostStream'
-class SearchView extends PureComponent {
- render() {
- return Search View Page(todo)
- }
+const useStyles = makeStyles((theme) => ({
+ root: {
+ padding: 16,
+ backgroundColor: '#f0f2f5',
+ minHeight: '100vh',
+ },
+ controls: {
+ display: 'flex',
+ justifyContent: 'center',
+ marginBottom: 16,
+ gap: 8,
+ },
+ iconsContainer: {
+ marginTop: theme.spacing(2),
+ display: 'flex',
+ justifyContent: 'center',
+ gap: 8,
+ },
+ searchBar: {
+ display: 'flex',
+ alignItems: 'center',
+ width: '100%',
+ borderRadius: theme.shape.borderRadius,
+ backgroundColor: theme.palette.common.white,
+ padding: theme.spacing(1, 2),
+ marginBottom: theme.spacing(2),
+ border: '1px solid #ddd',
+ },
+ input: {
+ marginLeft: theme.spacing(1),
+ flex: 1,
+ },
+ iconButton: {
+ padding: 10,
+ color: theme.palette.text.secondary,
+ },
+ icon: {
+ margin: theme.spacing(0, 1),
+ color: theme.palette.text.secondary,
+ fontSize: '1.5rem',
+ transition: 'all 0.2s ease-in-out',
+ '&:hover': {
+ transform: 'scale(1.1)',
+ backgroundColor: theme.palette.action.hover,
+ },
+ },
+ activeFilter: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ '&:hover': {
+ backgroundColor: theme.palette.primary.dark,
+ },
+ },
+ datePickerContainer: {
+ padding: '16px',
+ backgroundColor: 'white',
+ borderRadius: '8px',
+ boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
+ maxWidth: 400,
+ margin: '0 auto',
+ marginBottom: 16,
+ },
+ datePickerInput: {
+ '& .react-datepicker-wrapper': {
+ width: '100%',
+ },
+ '& .react-datepicker__input-container input': {
+ width: '100%',
+ padding: '12px 16px',
+ border: '1px solid #ddd',
+ borderRadius: '4px',
+ fontSize: '14px',
+ fontFamily: theme.typography.fontFamily,
+ '&:focus': {
+ outline: 'none',
+ borderColor: theme.palette.primary.main,
+ boxShadow: `0 0 0 2px ${theme.palette.primary.main}20`,
+ },
+ },
+ },
+}))
+
+function SearchView() {
+ const classes = useStyles()
+ const loggedIn = useSelector((state) => !!state.user.data._id)
+ const [paused, setPaused] = useState(false)
+ const [filterMode, setFilterMode] = useState('all')
+ const [isCalendarVisible, setIsCalendarVisible] = useState(false)
+ const [dateRangeFilter, setDateRangeFilter] = useState({ startDate: null, endDate: null })
+ const [searchKey, setSearchKey] = useState('')
+ const [streamEnabled, setStreamEnabled] = useState(loggedIn)
+
+ const togglePause = () => setPaused((p) => !p)
+ const handleFriendsFilter = () => setFilterMode((m) => (m === 'friends' ? 'all' : 'friends'))
+ const handleInteractionsFilter = () => setFilterMode((m) => (m === 'interactions' ? 'all' : 'interactions'))
+ const handleCalendarToggle = () => setIsCalendarVisible((v) => !v)
+ const handleDateChange = ([startDate, endDate]) => setDateRangeFilter({ startDate, endDate })
+ const clearDateFilter = () => setDateRangeFilter({ startDate: null, endDate: null })
+
+ useEffect(() => {
+ if (
+ streamEnabled &&
+ (
+ searchKey ||
+ filterMode !== 'all' ||
+ dateRangeFilter.startDate ||
+ dateRangeFilter.endDate
+ )
+ ) {
+ setStreamEnabled(false)
+ }
+ }, [searchKey, filterMode, dateRangeFilter.startDate, dateRangeFilter.endDate, streamEnabled])
+
+ const showLiveStream =
+ streamEnabled &&
+ loggedIn &&
+ !searchKey &&
+ filterMode === 'all' &&
+ !dateRangeFilter.startDate &&
+ !dateRangeFilter.endDate
+
+ return (
+
+
+
+ {paused ? : }
+
+
{paused ? 'Stream paused' : 'Live stream'}
+
+
+
e.preventDefault()}>
+ setSearchKey(e.target.value)}
+ />
+
+
+
+
+
+
+
+ 👥
+
+
+ 🔥
+
+
+ 📅
+
+
+
+ {isCalendarVisible && (
+
+
+ handleDateChange([date, dateRangeFilter.endDate])}
+ selectsStart
+ startDate={dateRangeFilter.startDate}
+ endDate={dateRangeFilter.endDate}
+ maxDate={dateRangeFilter.endDate || new Date()}
+ dateFormat="MMM d, yyyy"
+ placeholderText="Select start date"
+ />
+
+
+ handleDateChange([dateRangeFilter.startDate, date])}
+ selectsEnd
+ startDate={dateRangeFilter.startDate}
+ endDate={dateRangeFilter.endDate}
+ minDate={dateRangeFilter.startDate}
+ maxDate={new Date()}
+ dateFormat="MMM d, yyyy"
+ placeholderText="Select end date"
+ />
+
+
+
+ )}
+
+ {showLiveStream && (
+
+ )}
+
+ )
}
export default SearchView