How do I control the rendering of video endpoint in a video conference? - voximplant

How do I control the rendering of video endpoint in a video conference? I would like to render it in my own way, change the video size and more. Also, I cannot understand, how the IDs are named for the video elements. Are they the same with the endpoint IDs?

To process the video elements yourself, you need to:
first subscribe to remote media added/removed events:
https://voximplant.com/docs/references/websdk/voximplant/endpointevents#remotemediaadded
https://voximplant.com/docs/references/websdk/voximplant/endpointevents#remotemediaremoved
then process the render event:
https://voximplant.com/docs/references/websdk/voximplant/mediarenderer#render
The video elements IDs are not the same as endpoint IDs. The elements IDs are the same name as video track ID. There is no internal way in Voximplant to get the element ID, but you still can get it. Here is a custom function that will allow you to get the IDs:
const getMediaElementIds = (call) => {
return call.getEndpoints().map((endpoint) => {
return endpoint.mediaRenderers.reduce(
(acc, mr) => {
const key = mr.kind === 'audio' ? 'audioElementId' : 'videoElementId';
const id = mr.stream.getTracks()[0]?.id;
return { ...acc, [key]: id };
},
{ endpointId: endpoint.id }
);
});
};
// returns { endpointId: string; videoElementId?: string; audioElementId?: string }[]
And by the way, there are some nuances for Vue.js rendering:
don't place the sdk objects (like client, endpoints, calls etc) into immutable stores like Vuex, Rx.js and so on.
render the MediaRenderer into a container with v-once, so Vue.js does not rerender this part

Related

Nested data in RTKQuery hook getting join data

I am implementing the "favorites" logic in a project where there are some cards showing some info about songs, and a heart icon shows red if that song is in the user favorites or empty if it is not.
I am using Supabase as a backend (SQL) and I already have a "profiles" table, a "songs" table and a "favorites" table with two foreign keys: profile_id and song_id.
In the project I am using RTKQuery and I have already configured the endpoint to obtain the data for the favorites which is like:
endpoints: (builder) => ({
getFavorites: builder.query({
queryFn: async (id) => {
const {data, error} = await supabase
.from('favorites')
.select(`users_id,songs_id (*)`)
.eq('users_id',id)
return { data, error }
},
providesTags: ["Favorites"]
}),
Then I get the data with:
const { data, isSuccess } = useGetFavoritesQuery( id );
I am calling this hook from the component mad for each card, which already have the props. So I can check if the prop "song.id" is in some of the ids inside the "favorites" object.
But the problem I am facing is because the data that I really need is the nested object inside data, and each one of the nested objects have the id that I would use to check if it is === to song.id of the component I am calling from.
I have tried several things but I don't know if I am doing fine. This is one solution that I have tried. First I iterate over data to obtain the object "favorites":
let favorites;
if (isSuccess){
data.forEach(element => {
favorites = element.songs_id;
})
}
And then the boolean for the heart red or empty:
const favoriteIconColor = useMemo(() => {
if (Object.values(favorites).some(item=> item.id === song.id)){
return true
}
} , [favorites, song]);
But that is not working. I am thinking about creating just an array with all of the ids in the first iteration, but I am not sure if I am overcomplicating the issue.
I have also considered that maybe there is a way to obtain the data as I need with the query itself, but not sure about it. Any advice?

Ngrx waiting for Multiple API calls in effect

I'm super new about ngrx and I'm trying to develop an effect to dispatch multiple calls to my API to retrive a child list of objects.
Here my code.
loadMyChildren$ = createEffect (() => this.actions$.pipe(
ofType(SomeActions.loadMyChildren),
switchMap(({parentsObjArr}) => {
const obsList$: Observable<ChildModel>[] = parentsObjArr.result.map(parentsObj => this.childrenService.loadTimeSeries(parentsObj));
let childrenArr: ChildModel[] = [];
const source$ = zip(obsList$);
source$.subscribe((res) =>{
childrenArr = res;
}, err => { console.log(err); });
console.log('childrenArr', childrenArr)
return [
SomeActions.loadChildrenSuccess({childrenArr}),
SomeActionsTwo.loadParentsSuccess({parentsObjArr})
]
}),
catchError((err) => {
return of(SomeActions.loadMyChildrenFailed)
})
)
Unfortunately my effect seem doesn't wait for my API requests complete on "zip" (I tried also combineLatest and forkJoin) before dispatching the actions on the return array... "console.log" of my objects array is always empty and the store, consequently, does not keep any data.
What I'm wrong?
rxjs 6.6.0
angular 13.2.3
It's not good practise to manualy subscribe inside switchMap.
You are right, your code returns action before your API requests completes, you need to manipultate the stream without subscribing inside.
I would refactor your coude, so source$ is returned in switchMap, but its result is maped to desired ngrx actions with props that you wanted.
It would looked something like that:
switchMap(({parentsObjArr}) =>
zip(parentsObjArr.result.map(parentsObj => this.childrenService.loadTimeSeries(parentsObj)))
.pipe(
map(child => [
SomeActions.loadChildrenSuccess({child}),
SomeActionsTwo.loadParentsSuccess({parentsObjArr})
]
)
)
)

Getting card cover image with the Trello REST API with batching

I'm trying to get the cover image links for all cards from a board but the boards endpoint is only providing an attachment id for the cover image. That might be fine but I also don't see how to use that id for getting the full image link. This is my request:
${TRELLO_API_ROOT}/boards/${boardId}?key=${TRELLO_KEY}&token=${TRELLO_TOKEN}&cards=all&card_customFieldItems=true&attachments=true&attachment_fields=all
And this returns all of the cards and cover image details for each card but the information does not seem useful for getting the full image path:
cards: [{
cover: {
brightness: "light",
color: null,
idAttachment: "5eee7680d7b0295f6c52fc22",
idUploadedBackground: null,
size: "normal
}
}]
I have considered that I might need to make a request to the cards endpoint for each individual card using the batch process but that process is limited to 10 requests. A board could easily have more than 10 cards so this doesn't seem like a good solution.
Is it true that you need to send a request to each individual card in order to get the cover images?
EDIT: The only way that I see to do this is to make separate requests for each card against the attachments endpoint. This has the potential to be a lot of requests though:
const requests = data.cards.filter((item) => {
return item.cover.idAttachment;
}).map((card) => {
return fetch(`${TRELLO_API_ROOT}/cards/${card.id}/attachments/${card.cover.idAttachment}?key=${TRELLO_KEY}&token=${TRELLO_TOKEN}`)
})
Promise.all(requests)
.then(responses => {
return responses;
})
.then(responses => Promise.all(responses.map(res => res.json())))
.then(attachments => {
return attachments;
});
Answering my own question because I don't believe there is any other solution than to send an individual request for each card. But we can at least improve on it by using batch.
And one clarification; you only need to request the cover image attachment url if the card has been assigned an uploaded image as the cover. If the card cover is one of the available Unplashed images that Trello is offering now, you'll get that images url in the regular boards endpoint request as a sharedSourceUrl like this:
{
brightness: "light",
color: null,
edgeColor: "#776e5d",
idAttachment: null,
idUploadedBackground: "5efe2d6c4292a74f4c4051e0",
scaled: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}],
sharedSourceUrl: "https://images.unsplash.com/photo-1593435220813-8e60f12e947a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjcwNjZ9&w=2560&h=2048&q=90",
size: "normal"
}
I don't know if it's an oversite that they don't do the same thing automatically for uploaded image urls but there doesn't appear to be any other way to get the card image if it's an upload.
So using the batch process, you'll need to make sure to only send 10 urls at a time. This is simple enough with a standard loop to build out the arrays. Then we can queue up the batch requests into a Promise.all and handle the response by assigning that cover url to the corresponding card. We can reduce are requests significantly this way but it would still have been nice to have the cover url available during the first boards fetch.
fetch(`${TRELLO_API_ROOT}/boards/${boardId}?cards=visible&card_customFieldItems=true&key=${TRELLO_KEY}&token=${TRELLO_TOKEN}&attachments=true&attachment_fields=all`)
.then(response => response.json())
.then((data) => {
const cardsWithCovers = data.cards.filter((item, i) => {
return item.cover.idAttachment;
});
// Creating our batched request array for fetching cover urls.
const batchedUrls = [];
let batch = [];
// Creating batches.
for (let i = 0; i < cardsWithCovers.length; i++) {
// Important here not to append the url root, api key, or token values. That's how the batch endpoint wants it.
const url = `/cards/${cardsWithCovers[i].id}/attachments/${cardsWithCovers[i].cover.idAttachment}`;
batch.push(url);
// If we have our max 10 request urls, or it's the last item, push the request url group and reset batch array.
if (batch.length === 10 || i === cardsWithCovers.length - 1) {
batchedUrls.push(batch);
batch = [];
}
}
const requests = batchedUrls.map((urls) => {
return fetch(`${TRELLO_API_ROOT}/batch?key=${TRELLO_KEY}&token=${TRELLO_TOKEN}&urls=${urls}`)
});
Promise.all(requests)
.then(responses => Promise.all(responses.map(res => res.json())))
.then(batchedData => {
// Combine the batched array responses into a single array.
const mergedCardsWithCovers = [].concat.apply([], batchedData);
data.cards.forEach((card) => {
// Finding the card cover image url and adding it to the card.
const cover = mergedCardsWithCovers.find((cardWithCover) => {
return card.cover.idAttachment === cardWithCover[200].id;
});
if (cover) card.cover.url = cover[200].url;
});
})
})

Upload image need to refresh using $emit / $on in Vue

I have a method that calls the user data via axios
// method name getUser()
const user = await axios.get(`/user/${this.id}`)
this.id = user.data.data.id
this.name = user.data.data.name
this.email = user.data.data.email
I then use that in the mounted so if user visits /profile/id
it'll load the user data
mounted() {
this.getUser()
}
I tried to upload an image and I emit the event using global event bus once the image is successfully uploaded.
this.$event.$emit('IMAGE_UPLOAD')
Then catch that on the mounted too
mounted () {
// if I remove this it works, but I need to preload the data of the user
this.getUser()
this.$event.$on('IMAGE_UPLOAD', () => {
this.getUser()
})
}
my problem is it doesn't change the image meaning I still need to refresh the page if I call the this.getUser() too inside the mounted.
So I'm wondering how to work around this.
Thanks!
Since the url and name of the image does not change when the new image is uploaded the image in the browser is not updated. So what I have done in the past is a little trick to essentially change the url to the image by adding a unique query parameter. So use a data property for the location of your user image and in your method where you update the users data also update the img url and add something unique to the query parameter. I usually use new Date().getTime(). So you will end up with something like /img/user-xxxx.png?1559289852686
data(){
userImg: '/img/user-xxxx.png'
},
methods:{
getUser(){
//... get your user data
.then((data)=>{
this.userImg = data.user.img +'?'+ new Date().getTime();
})
}

Vuexfire bindFirebaseRef with pagination infinite scroll

Question: How can I add pagination (infinite scroll) to my binded Firestore VuexFire reference without re-querying previously retrieved (and binded) results?
Background:
I am currently using VuexFire firestore binding to fill a timeline for most upvoted posts, as an action, in my Vuex store like this:
fillTimeLine: firebaseAction(
({ bindFirebaseRef }) => {
bindFirebaseRef(
'timelineResults',
db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.limit(30)
)
})
This will retrieve the top 30 highest rated posts in my firestore database to my vuex state variable timelineResults.
To add pagination I have found a non-VuexFire example like this:
How to paginate or infinite scroll by number of items in firestore?
var first = db.collection("....").orderBy("price", "desc").limitTo(20);
return first.get().then(function (documentSnapshots) {
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
console.log("last", lastVisible);
// Construct a new query starting at this document,
// get the next 25 cities.
var next = db.collection("....")
.orderBy("price", "desc")
.startAfter(lastVisible)
.limit(20);
});
Is there a way to combine the two examples and append results to a binded reference?
You could create a more generic action, just like this:
bindRef: firestoreAction(({ bindFirestoreRef }, { name, ref }) => {
bindFirestoreRef(name, ref);
}),
And then using it like:
this.bindRef({
name: 'timelineResults',
ref: db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.limit(30),
});
There you can change the ref according to your needs. In this case, when you detect the scroll limit:
// lastVisible: using the array position from the previous binding
// since with vuex's bound data you cannot get the snapshots
this.bindRef({
name: 'timelineResults',
ref: db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.startAfter(lastVisible)
.limit(20),
});