Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pagination Feature #46

Merged
merged 9 commits into from
Mar 19, 2020
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"react-places-autocomplete": "^7.2.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"react-scroll-up-button": "^1.6.4",
"react-share": "^4.1.0",
"react-with-firebase-auth": "^1.3.0",
"tailwindcss": "^1.2.0"
Expand Down
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import fb from './firebase';
import SuccessOffer from './views/SuccessOffer';
import DSGVO from './views/DSGVO';
import CookieConsent from 'react-cookie-consent';
import ScrollUpButton from "react-scroll-up-button";
import MenuIcon from '@material-ui/icons/Menu';
import {
HashRouter as Router,
Expand Down Expand Up @@ -124,6 +125,7 @@ function App (props) {
<Main/>
</Route>
</Switch>
<ScrollUpButton/>
</div>
</div>
</div>
Expand Down
145 changes: 112 additions & 33 deletions src/components/FilteredList.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,134 @@ import LocationInput from './LocationInput';
import {isMapsApiEnabled} from '../featureFlags.js';
import {Link} from 'react-router-dom';

export default function FilteredList() {
export default function FilteredList(props) {

const {
pageSize = 0
} = props;

const [searching, setSearching] = useState(false);
const [location, setLocation] = useState('');
const [entries, setEntries] = useState([
{
id: 'placeholder-id',
}]);
const [filteredEntries, setFilteredEntries] = useState([
{
id: 'placeholder-id',
}]);
const [entries, setEntries] = useState([]);
const [scheduledSearch, setScheduledSearch] = useState([]);

const [lastEntry, setLastEntry] = useState(undefined);

const collection = fb.store.collection('ask-for-help');
const query = collection.orderBy('d.timestamp', 'desc');

const getUserData = () => {
// Create a Firestore reference
const geofirestore = new GeoFirestore(fb.store);

// Create a GeoCollection reference
const geocollection = geofirestore.collection('ask-for-help');

const buildQuery = async (location = undefined, lastLoaded = undefined, limit = pageSize) => {
var queryResult;

if (searching) {
setEntries([]);
if(!location) {
setSearching(false);
}
} else if (!searching && location) {
setEntries([]);
setSearching(true);
}

//If map api is available,
if (isMapsApiEnabled && location && location !== '') {
queryResult = geocollection;

try {
var results = await geocodeByAddress(location);
var coordinates = await getLatLng(results[0]);
queryResult = queryResult.near({ center: new fb.app.firestore.GeoPoint(coordinates.lat, coordinates.lng), radius: 30 });
} catch (error) {
queryResult = collection.orderBy('d.timestamp', 'desc');
console.error('Error', error);
}
} else {
queryResult = collection;

if (location && location !== '') {
queryResult = queryResult.orderBy('d.plz', 'asc');
queryResult = queryResult.startAt(location).endAt(location + "\uf8ff");
} else {
queryResult = queryResult.orderBy('d.timestamp', 'desc');

if (lastLoaded !== undefined) {
queryResult = queryResult.startAfter(lastLoaded);
if (limit > 0) queryResult = queryResult.limit(limit);
} else {
if (limit > 0) queryResult = queryResult.limit(limit);
}
}
}

return queryResult;
};


const initialize = async () => {
var query = await buildQuery();
query.get().then(value => {
setEntries(value.docs.map(doc => ({...doc.data().d, id: doc.id})));
setFilteredEntries(value.docs.map(doc => ({ ...doc.data().d, id: doc.id })));
appendDocuments(value.docs)
});
};

useEffect(getUserData, []);
useEffect(() => {
initialize();
}, []);

// Create a Firestore reference
const geofirestore = new GeoFirestore(fb.store);
const loadMore = async () => {
var query = await buildQuery(undefined, lastEntry);
query.get().then(value => {
appendDocuments(value.docs);
})
}

// Create a GeoCollection reference
const geocollection = geofirestore.collection('ask-for-help');
const loadFilteredData = async (queryPromise) => {
var query = await queryPromise;
query.get().then(value => {
appendDocuments(value.docs)
});
}

const appendDocuments = (documents) => {
setLastEntry(documents[documents.length - 1]);
var newEntries = documents.map(doc => {
var data = doc.data();
return { ...(data.d || data), id: doc.id }
});
setEntries(entries => ([...entries, ...newEntries]));
}

const handleChange = address => {
setLocation(address);
if (!isMapsApiEnabled) {
setFilteredEntries(entries.filter(entry => String(entry.plz).indexOf(address) === 0));
if(scheduledSearch) {
clearTimeout(scheduledSearch);
}
setScheduledSearch(setTimeout(() => {
loadFilteredData(buildQuery(address));
}, 500));
}
};

const handleSelect = address => {
setLocation(address);
if (isMapsApiEnabled) {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(coordinates => {
const query = geocollection.near({ center: new fb.app.firestore.GeoPoint(coordinates.lat, coordinates.lng), radius: 30 });
query.get().then((value) => {
// All GeoDocument returned by GeoQuery, like the GeoDocument added above
setEntries(value.docs.map(doc => ({ ...doc.data(), id: doc.id })));
setFilteredEntries(value.docs.map(doc => ({ ...doc.data(), id: doc.id })));
});
})
.catch(error => console.error('Error', error));
if(scheduledSearch) {
clearTimeout(scheduledSearch);
}
setScheduledSearch(setTimeout(() => {
loadFilteredData(buildQuery(address));
}, 500));
}
};

const NoHelpNeeded = (props) => {
return <div className="w-full text-center my-10">In {location} wird gerade keine Hilfe gebraucht!</div>
return <div className="w-full text-center my-10 font-open-sans">In {location} wird gerade keine Hilfe gebraucht!</div>
};

return (<div>
Expand All @@ -74,9 +146,16 @@ export default function FilteredList() {
<Link to='/notify-me' className="btn-green-secondary my-3 mb-6 w-full block" onClick={() => fb.analytics.logEvent('button_subscribe_region')}>
Benachrichtige mich wenn jemand in {location && location !== '' ? `der Nähe von ${location}` : 'meiner Nähe'} Hilfe braucht!</Link>
</div>
{filteredEntries.length === 0 ? <NoHelpNeeded /> : filteredEntries.map(
{entries.length === 0 ? <NoHelpNeeded /> : entries.map(
entry => (
<Entry key={entry.id} {...entry}/>))}
<Entry key={entry.id} {...entry}/>))
}
{(pageSize > 0 && !searching) ? <div className="flex justify-center pt-3">
<button onClick={loadMore} className="items-center rounded py-3 px-6 btn-main btn-gray md:flex-1 hover:opacity-75">
Weitere anzeigen...
</button>
</div> : null
}
</div>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/styles/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
letter-spacing: 0;
}

.btn-gray {
color: #777777;
background-color: #eeeeee;
}

.font-open-sans {
font-family: 'Open Sans', sans-serif;
letter-spacing: 0;
Expand Down
7 changes: 1 addition & 6 deletions src/views/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,7 @@ export default function Main () {
Gib deine Postleitzahl ein, um hilfesuchende Menschen in deinem Umkreis zu finden.
</div>
</div>
<FilteredList/>
<div className="flex justify-center items-center">
<Link to="/overview" className="btn-green">
ALLE ANFRAGEN
</Link>
</div>
<FilteredList pageSize={20}/>
</div>
<Footer/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/views/Overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function AskForHelp () {
Drucke <a href='/assets/aushang.pdf' className="text-secondary hover:underline" download="/assets/aushang.pdf">diesen Aushang</a></p>
</div>
<div className="py-3">
<FilteredList />
<FilteredList pageSize={20}/>
</div>
<Footer />
</div>
Expand Down