Fetch and display lists of an user - firebase

I have a profile page that displays the user info. The page shows the user name / email and a button to create a list.
I can also edit the name and email correctly, and it reflects in the firebase instantaneously. Ok. I get the user data and I can edit it.
What I'm trying to do now is to show the lists that the user has created.
Look, this user has created one list, and what is returned to me is that he doesn't have lists.
I'll try to shorten the code as much as possible:
<script>
imports.....
import { db } from '../../firebase.config.js'
let listings = []
let auth = getAuth()
// fetch the user's listings
const fetchUserListings = async () => {
const listingsRef = collection(db, 'listings')
const q = query(
listingsRef,
where('userRef', '==', auth.currentUser.uid),
orderBy('timestamp', 'desc')
)
const querySnap = await getDocs(q)
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
}
fetchUserListings()
</script>
<!-- display the user's listings -->
<div>
{#if listings.length > 0}
<p class="listingText">My lists</p>
{#each listings as listing}
<ListingItem listing={listing.data} id={listing.id} />
{/each}
{:else}
<p class="noListings">You have no lists</p>
{/if}
</div>
My ListItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer ? listing.discountedPrice : listing.regularPrice}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
Just when you think you've reached the simplest part, it's still tough.
Update:
I think that the problem is in firebase. The "docs" are empty:
Now I am in serious trouble!

querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
I see two things here. The less important: The .forEach() method returns undefined, so the return is redundant. The more important: the .push() alone won't automatically trigger updates. Have a look at this section in the Docs
Did you try logging listings? I assume the data is there, it's just not displayed, so I propose to change this part to
querySnap.forEach((doc) => {
listings = [...listings, {
id: doc.id,
data: doc.data()
}]
})
or
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data()
})
listings = listings
})

Related

When I make a request to the server with query parameters nothing changes. A different number of users should be displayed

In this project I am using FireBase database. I need to change the number of users per page for example 5, Change the number of pages according to the total number of users / number of users per page. To do this, I change the query params, but it does not want to change. I don't know how to enter the parameters correctly so that they appear to me as they should, maybe I didn't write them correctly or I need to write them differently
import React, {Component} from "react";
import classes from "./users.module.css";
import * as axios from "axios";
import userPhoto from "../../assect/image/standartAvatar.jpg";
class Users extends Component {
componentDidMount() {
axios
.get(
`https://kabzda-cff30-default-rtdb.europe-west1.firebasedatabase.app/users.json?page=${this.props.currentPage}&count=${this.props.pageSize}`
)
.then((response) => {
this.props.setUsers(response.data.items);
this.props.setTotalUsersCount(response.data.totalCount);
})
}
onPageChanged = (pageNumber) => {
this.props.setCurrentPage(pageNumber)
axios
.get(
`https://kabzda-cff30-default-rtdb.europe-west1.firebasedatabase.app/users.json?page=${pageNumber}&count=${this.props.pageSize}`
)
.then((response) => {
this.props.setUsers(response.data.items);
})
}
render() {
let pagesCount = Math.ceil(this.props.totalUsersCount / this.props.pageSize)
let pages = [];
for(let i = 1; i <= pagesCount; i++){
pages.push(i)
}
return (
<div>
<div className={classes.counter}>
{pages.map(p => (
<span className={(this.props.currentPage === p && classes.selectedPage) || classes.pointer}
onClick={() => {this.onPageChanged(p)}}
>
| {p} |</span>
))}
</div>
{this.props.users.map((u, i) => (
<div key={i+1}>
<span>
<div>
<img
src={u.photos.small !== "" ? u.photos.small : userPhoto}
className={classes.userPhoto}
alt="avatar"
/>
</div>
<div>
{u.followed ? (
<button
onClick={() => {
this.props.unfollow(u.id);
}}
>
UnFollow
</button>
) : (
<button
onClick={() => {
this.props.follow(u.id);
}}
>
Follow
</button>
)}
</div>
</span>
<span>
<span>
<div>{u.name}</div>
<div>{u.status}</div>
</span>
<span>
<div>{"u.location.country"}</div>
<div>{"u.location.city"}</div>
</span>
</span>
</div>
))}
</div>
);
}
}
export default Users;

how can i order by snapshot's child data containing a timestamp index with vue js

I saved the timestamp as a data field inside firebase DB, this way I can now retrieve it like {{ post.timestamp }} to display it, how would I go from where I am at to order the posts by timestamp order regardless of the user object order, for example, what I get in the UI is the posts ordered by the user and not by time.
data on firebase looks like this:
Code looks like this :
<template>
<div>
<div
v-for="post in allPosts.slice().reverse()"
:key="post._key">
<v-card class=" feed-card my-3">
<v-row no-gutters>
<v-col cols="1">
<v-img
class="align-center rounded-xl "
width="30"
:src="post.photoURL">
</v-img>
</v-col>
<v-col cols="10">
<p class="">{{post.postText}}</p>
<p class="blue-grey--text ">{{post.displayName}}</p>
<p class="mb-n1 mt-n5 d-flex flex-row-reverse"> {{post.date}} {{post.time}}</p>
</v-col>
</v-row>
</v-card>
</div>
</div>
</template>
<script>
import firebase from '#/plugins/firebase'
let db = firebase.database();
//let usersRef = db.ref('users');
let postRef = db.ref('posts');
export default {
name: 'FintechSocialFeed',
data: () => ({
authUser: null,
allPosts: [] // initialise an array
}),
methods: {
},
created: function() {
data => console.log(data.user, data.credential.accessToken)
firebase.auth().onAuthStateChanged(user => {
if (user) {
postRef.on('value', snapshot => {
const val = snapshot.val()
if (val) {
this.allPosts = Object.values(val).flatMap(posts =>
Object.entries(posts).map(([ _key, post ]) => ({ _key, ...post})))
}
console.log(snapshot.val())
});
}
})
}
}
</script>
here is the UI showing the latest post at the bottom because it is sorting by the user and not date:
I don't use firebase, but it looks like db reference provides orderByKey, so...
let postRef = db.ref('posts').orderByKey('timestamp');
An alternative would be sorting yourself, after retrieval...
this.allPosts = Object.values(val).flatMap(posts =>
Object.entries(posts).map(([ _key, post ]) => ({ _key, ...post}))
).sort((a, b) => a.timestamp.toMillis() - b.timestamp.toMillis());

make input search box full screen overlay on click on mobile device

I am using buefy autocomplete input fields in my nuxtjs project, this is location search box, what i want is just for mobile device, when i click the input field, it should overlay on full screen with suggestion like i attached screenshot below and after select suggestion, it should close and return to normal.
here is my simple auto complete input field code.
<template>
<b-autocomplete
v-model="pickupairport"
:data="airports"
name="pickupairport"
class="ttc-search-input"
icon="map-marker-outline"
placeholder="Pickup Airport"
field="name"
:loading="isFetching"
#typing="getairports"
#select="(option) => (aptselected = option)"
>
<template slot-scope="props">
<div class="media">
<div class="media-content">
{{ props.option.name }}
<br />
<small> {{ props.option.cityName }}, {{ props.option.countryName }} </small>
</div>
</div>
</template>
</b-autocomplete>
</template>
<script>
import { debounce } from 'lodash'
export default {
data() {
return {
pickupairport: '',
airports: [],
aptselected: null,
isaptFetching: false,
}
},
methods: {
getairports: debounce(function (pickupairport) {
const aptsearchq = this.pickupairport
if (!pickupairport.length) {
this.airports = []
return
}
this.isaptFetching = true
fetch(`https://api.myurl.com/api/transfers/aplist?querystring=${aptsearchq}`)
.then((response) => {
return response.json()
})
.then((data) => {
this.airports = []
data.response.forEach((item) => this.airports.push(item))
})
.catch((error) => {
this.airports = []
throw error
})
.finally(() => {
this.isaptFetching = false
})
}, 500),
},
}
</script>
What I want to achieve is like this GIF - https://i.imgur.com/zOYPwBI.gif
What I have now is like this GIF - https://imgur.com/9ZBZzxa
i tried to find something related, but couldn't find, if any suggestion on how to achieve that, it would be helpful for me.

vue js realtime chat app without refreshing / firebase

I'm creating a chat app,
If the user enter message and press send button, the app is working fine. The informations going to the database and im taking a datas.
When the user refresh the page, so there is no problem, in mounted() instance im taking the datas from database(firebase) and im showing on the app. If another user comes to the chat, also there is no problem, all messages are appearing.
The problem is that: If the new message is coming, another user can not see it without refresh or without send message button. When the another user send a message then the user see all messages.
I explain the problem with a gif, if you help me i will be glad.
<template>
<div>
<div class="container">
<div class="row">
<div v-if="isLogin" class="offset-3 col-md-6 msg-area">
<header>
<h1>Group Chat</h1>
<p class="sm">Welcome, {{this.username }} </p>
</header>
<div class="msg">
<p class="mssgs" v-for="(message,index) in messages" :key="index">{{ message.msg }} <br> <span> {{ message.name }} - {{ message.time }} </span> </p>
</div>
<div class="sendMsg">
<form #submit.prevent="sendFunc">
<div class="form-group d-flex">
<input type="text" class="form-control" placeholder="Enter message.." v-model="msgInput">
<button class="btn">Send </button>
</div>
</form>
</div>
</div>
<div class="offset-3 col-md-6 login" v-else>
<form #submit.prevent="joinFunc">
<div class="form-group d-flex">
<input type="text" class="form-control" placeholder="Enter username.." v-model="username">
<button class="btn">Join to Group </button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import firebase from "./firebase";
import 'firebase/firestore';
export default {
data() {
return {
db : firebase.firestore(),
isLogin: false,
username: '',
messages : [
],
msgInput: '',
}
},
methods: {
joinFunc() {
this.isLogin = true;
},
sendFunc() {
let date = new Date();
let hour = date.getHours();
let minute = date.getMinutes();
let nowTime = hour + ':' + minute;
this.db.collection("messages")
.add({ message: this.msgInput, name: this.username, time: nowTime, date: date })
.then(() => {
this.db.collection("messages").orderBy("date", "asc")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.messages.push({
name: doc.data().name,
msg: doc.data().message,
time: doc.data().time
});
});
})
})
.catch((error) => {
console.error("Error writing document: ", error);
});
},
},
mounted: function() {
this.db.collection("messages").orderBy("date", "asc")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.messages.push({
name: doc.data().name,
msg: doc.data().message,
time: doc.data().time
});
});
})
}
}
</script>
You're using get() to read the data from Firestore. As the documentation I linked explains, that reads the value from the database once, and does nothing more.
If you want to continue listening for updates to the database, you'll want to use a realtime listener. By using onSnapshot() your code will get called with a querySnapshot of the current state of the database right away, and will then also be called whenever the database changes. This is the perfect way to then update your UI.
So instead of
...
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
Do the following:
...
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {

Pushing data to object in different component using POST

TL;DR I want to show submitted posts instantly instead of having to refresh my page
Using the Wordpress REST API I am able to create a new post without any issue. The post is being displayed as soon as the page refreshes, so what I want to do is update the posts object in my Hello.vue file as soon as I create that post so I don't need to refresh to show my newest posts.
I'm not really sure where to start - I've removed all of the experiments I've done so far (importing Post in Create, defining props, pushing to an array, reading about object reactivity on the official Vue documentation, nothing helped).
My App.js consists of the <router> object which shows Hello.vue and a component called Create which displays the Create.vue component. This is how my app currently looks like:
My App.vue file:
<template>
<div id="app">
<section class="posts">
<router-view></router-view>
<create></create>
</section>
</div>
</template>
<script>
import Create from '#/components/Create.vue'
export default {
name: 'app',
components: {
Create
}
}
</script>
<style lang="scss">
#import '../src/assets/styles/style.scss'
</style>
My Hello.vue which displays all the posts:
<template>
<div>
<section class="posts__Feed">
<ul class="posts__List">
<post v-for="item in posts" :item="item" :key="item.id"></post>
</ul>
</section>
</div>
</template>
<script>
var postsUrl = '/wp-json/wp/v2/posts/'
import Post from '#/components/Post.vue'
export default {
name: 'hello',
props: ['responseData'],
components: {
Post
},
data () {
return {
posts: []
}
},
beforeCreate () {
this.$http.get(postsUrl).then((response) => {
this.posts = response.data
})
}
}
</script>
And finally, the Create.vue file which creates the post:
<template>
<div>
<section class="posts__Create">
<form class="posts__CreateForm" v-on:submit="createPosts">
<div class="posts__CreateFormWrapper" v-bind:class="{ 'is-Loading': loading }">
<p>
<input v-model="formInfo.title" type="text" name="title" id="title" placeholder="Name" :disabled="formSent">
</p>
<p>
<textarea v-model="formInfo.content" name="content" id="content" cols="20" rows="10" maxlength="140" placeholder="Message" :disabled="formSent"></textarea>
</p>
<p>
<button :disabled="formSent">Send</button>
</p>
</div>
</form>
</section>
</div>
</template>
<script>
var postsUrl = '/wp-json/wp/v2/posts/'
export default {
name: 'create',
data () {
return {
formInfo: [],
responseData: [],
loading: false,
formSent: false
}
},
methods: {
createPosts (e) {
e.preventDefault()
var info = this.formInfo
// Check if fields are empty
if (this.formInfo.title && this.formInfo.content) {
this.loading = true
// POST
this.$http.post(postsUrl, info).then((response) => {
this.formSent = true
this.loading = false
// get body data
this.responseData = response.data
})
}
} // EOF createPosts
}
}
</script>
Any help would be much appreciated!
I ended up using an event bus as suggested by wotex. First, I've createad a file called bus.js with the below code:
import Vue from 'vue'
export const EventBus = new Vue()
Next, import bus.js to both .vue layouts using:
import { EventBus } from '#/bus.js'
Now emit the event as soon as a new post is created (this is sitting in my axios POST request inside the Create.vue file):
EventBus.$emit('newPost', this.responseData)
And finally, check if the event has happened on the other end (my Hello.vue file):
EventBus.$on('newPost', function (postData) {
Thanks for pointing me in the right direction!

Resources