Mobx state tree observe not working as expected - mobx

const Player = types.model({
game_object: types.frozen()
});
const Zone = types.model({
players: types.map(Player)
})
.actions(self => ({
addPlayer(params) {
const playerGameObject = new PlayerGameObject(params);
const newPlayer = Player.create({ game_object: playerGameObject })
self.players.set(params.socket_id, newPlayer)
}
}))
Later on I have
observe(store.zone, "players", change => {
console.log("Store zone players changed!!", change)
})
I am not sure why this isn't working. I've tried a similar thing with a string field instead of a Player and it did work!
Here's the code sandbox!
https://codesandbox.io/s/frosty-wave-wiy74?file=/src/index.js

Related

Creating a Heatmap for Rooms - No Structureinfo

I'm working on a project in which I have to generate a heatmap for some sensors that are beeing rendered inside of a modell using forgeviewer. For the implementation I'm following this tutorial: https://forge.autodesk.com/en/docs/dataviz/v1/developers_guide/examples/create_heatmap_for_rooms/
The modell I'm using was generated through Revit and translated into .svf using the Model-Derivative-API.
My problem now is, that I cant get any room or level data from my model which are needed for the generation of the heatmap.
These lines always give me no rooms or levels, eventhough there are rooms shown in the viewers modellbrowser as shown in the picture below.
modellbrowser with rooms
const structureInfo = new Autodesk.DataVisualization.Core.ModelStructureInfo(viewer.model);
console.log("STRUCTUREINFO");
console.log(structureInfo);
...
const shadingdata= await structureInfo.generateSurfaceShadingData(devices);
console.log("SHADINGDATA");
console.log(shadingdata);
StructureInfo in console
ShadingData in console
Question now is: Why cant I get any room or level data and how can I fix this?
The only thing that came to my mind so far that I have tried was to convert the revit file into .nwd using navisworks and translating that file into .svf. But the results where the same.
Here is some more Code. Please note that the application is clientside only and wont go into production like this. I'm only creating a prototype for presentations.
export const initializeViewer = async (urn: string) => {
let viewer: Autodesk.Viewing.GuiViewer3D;
fetch("https://developer.api.autodesk.com/authentication/v1/authenticate", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_id: "ClinetID",
client_secret: "ClentSecret",
grant_type: "client_credentials",
scope: "viewables:read",
}),
}) .then((res) => res.json())
.then((value) => {
const options = {
document: urn,
env: "AutodeskProduction",
accessToken: value.access_token,
api: "derivativeV2",
};
var container = document.getElementById("viewer-container");
if (container !== null) {
viewer = new Autodesk.Viewing.GuiViewer3D(container, {
extensions: [],
});
}
Autodesk.Viewing.Initializer(options, function onInitialized() {
addEvents();
viewer.start();
Autodesk.Viewing.Document.load(urn, onSuccess, onFailure);
});
});
const addEvents = () => {
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => {
loadExtensions();
onModelLoaded(viewer);
});
....
....
async function onModelLoaded(viewer: Autodesk.Viewing.GuiViewer3D) {
const dataVizExtn: any | Autodesk.Extensions.DataVisualization = await viewer.loadExtension("Autodesk.DataVisualization");
...
const aecModelData = await viewerDocument.downloadAecModelData();
if (aecModelData) {
const levelsExt: any | Autodesk.AEC.LevelsExtension = await viewer.loadExtension("Autodesk.AEC.LevelsExtension", {
doNotCreateUI: true,
});
const floorData = levelsExt.floorSelector.floorData;
const floor = floorData[2];
levelsExt.floorSelector.selectFloor(floor.index, true);
}
const structureInfo = new Autodesk.DataVisualization.Core.ModelStructureInfo(viewer.model);
let roomDevices: Autodesk.DataVisualization.Core.RoomDevice[] = [];
devices.forEach((device) => {
let autodeskDevice: Autodesk.DataVisualization.Core.RoomDevice = {
id: device.id, // An ID to identify this device
position: device.position, // World coordinates of this device
sensorTypes: device.sensorTypes, // The types/properties this device exposes
type: "Thermometer",
};
roomDevices.push(autodeskDevice);
});
const heatmap = await structureInfo.generateSurfaceShadingData(roomDevices, undefined, "Rooms");
};
Looks your source model is RVT in Deutschland. If so, please use this code snippet instead.
const shadingdata = await structureInfo.generateSurfaceShadingData(devices, null, 'Räumen')
For RVT -> NWD/DWC, please check my blog post here Add Data Visualization Heatmaps for Rooms of non-Revit model part I - NWC
Querying Revit master views in the viewer:
const root = viewerDocument.getRoot();
const viewables = root.search({'type':'geometry', 'role': '3d'});
console.log('Viewables:', viewables);
const phaseViews = viewables.filter(v => v.data.name === v.data.phaseNames && v.getViewableRootPath().includes('08f99ae5-b8be-4f8d-881b-128675723c10'));
console.log('Master Views:', phaseViews);
// or this one if you just have one master view (phase) inside your model.
// viewerDocument.getRoot().getDefaultGeometry(true);

Display date from Firestore React native

I try to display the picked event date för my posts from Firestore using moment. Right now it just print The current day, so I display the date but not ont he right way. I get no error message. I have tried to change the timestamp to "dateUpload". but it gives me a print in my Text component "invalid date". Do any have clue of what I can do?
This is how display it my flatlist:
postDate={moment(item.timestamp).format("ll")}
In my redux Action.js
export function fetchFollowingUsersPosts(uid) {
return ((dispatch, getState) => {
firebase.firestore()
.collection("posts")
.doc(uid)
.collection("userPosts")
.orderBy("creation", "asc")
.get()
.then((snapshot) => {
const uid = snapshot.query.EP.path.segments[1];
const user = getState().usersState.users.find(el => el.uid === uid);
const posts = snapshot.docs.map((doc) => {
const { data: firebaseTimestamp, ...rest } = doc.data()
const id = doc.id;
const data = firebaseTimestamp ? moment(firebaseTimestamp.toDate()) : null
return {
...rest,
id,
user,
...data
}
})
//console.log(posts);
dispatch({ type: USERS_POSTS_STATE_CHANGE, posts, uid, })
})
})
}
Image on my database:
Instead of const data = firebaseTimestamp ? moment(firebaseTimestamp.toDate()) : null, try this const data = firebaseTimestamp ? firebaseTimestamp.toDate() : null , so you'll be having a javascript Date object, instead of a Moment object.
Then, you can use it as you did postDate={moment(item.timestamp).format("ll")}, assuming that item.timestamp is the Date object from above

When state changes for graphql variable, result stays the same on react-native

I'm trying to create an app using shopify graphql api to create an ecommerce app on react native expo.
I have an onPress that calls a setState to change the state of the graphQL variable but the results don't change from the initial state of 'currentSubCategories'
const [currentSubCategories, setSubCategories] = useState(Categories[0].subCategory[0].handle);
let {
collection,
loading,
hasMore,
refetch,
isFetchingMore,
} = useCollectionQuery(currentSubCategories, first, priceRange);
const [currentCategory, setCategory] = useState({categories: Categories[0]});
const onSubCategorySelect = (subCategory) => { setSubCategories(subCategory.handle) }
onPress={() => onSubCategorySelect(item)}
function useCollectionQuery(
collectionHandle: string,
first: number,
priceRange: [number, number],
) {
let [isInitFetching, setInitFetching] = useState<boolean>(true);
let [isReloading, setIsReloading] = useState<boolean>(true);
let [collection, setCollection] = useState<Array<Product>>([]);
let isFetchingMore = useRef<boolean>(false);
let hasMore = useRef<boolean>(true);
let defaultCurrency = useDefaultCurrency().data;
let { data, loading, refetch: refetchQuery } = useQuery<
GetCollection,
GetCollectionVariables
>(GET_COLLECTION, {
variables: {
collectionHandle,
first,
sortKey: ProductCollectionSortKeys.BEST_SELLING,
presentmentCurrencies: [defaultCurrency],
},
notifyOnNetworkStatusChange: true,
fetchPolicy: 'no-cache',
});
let getMoreUntilTarget = async (
targetAmount: number,
cursor: string | null,
handle: string,
filter: [number, number],
) => {
let result: Array<Product> = [];
let moreData: Array<Product> = [];
let { data } = await refetchQuery({
first,
collectionHandle: handle,
after: cursor,
});
...
useEffect(() => {
if (!loading) {
isFetchingMore.current = false;
}
if (isInitFetching && !!data && !!data.collectionByHandle) {
let newCollection = mapToProducts(data.collectionByHandle.products);
hasMore.current = !!data.collectionByHandle?.products.pageInfo
.hasNextPage;
setCollection(newCollection);
setIsReloading(false);
setInitFetching(false);
}
}, [loading, isInitFetching]); // eslint-disable-line react-hooks/exhaustive-deps
return {
collection,
loading: isReloading,
hasMore: hasMore.current,
isFetchingMore: isFetchingMore.current,
refetch,
};
}
I'm using flatList to show the result
<FlatList
data={collection}
renderItem={({ item }) => (
<Text>{item.title}</Text>
)}
/>
According to docs you have to pass new variables to refetch otherwise refetch will use initial values.
In this case (custom hook) you have 2 ways to solvethis problem:
return variables from your custom hook (taken from useQuery),
return some own refetch function.
1st option needs 'manual' variables updating like:
refetch( { ...variablesFromHook, collectionHandle: currentSubCategories } );
In 2nd case you can create myRefetch (and return as refetch) taking collectionHandle parameter to call refetch with updated variables - hiding 'complexity' inside your hook.
Both cases needs refetch call after updating state (setSubCategories) so you should use this refetch inside useEffect with [currentSubCategories] dependency ... or simply don't use state, call refetch directly from event handler (in onSubCategorySelect).

Why can I add, but not remove an element from a set

I’m trying to update the notification count in my database.
I’m doing this by creating a set, which I add a UID to when I want to add to the notification count and removes a UID from the set when I want to subtract from the notification count.
I then take the size of the set and update the notification count.
the updateNotificationCount function is triggered by a lower order component.
However I can only get the database to update when isNewMatch is true. Why won’t it update the database when isNewMatch is false?
state = {notificationSet: new Set()}
updateNotificationCount = (uid, isNewMatch) => {
if (isNewMatch) {
this.setState(({ notificationSet }) => ({
notificationSet: new Set(notificationSet).add(uid)
}));
}
else {
this.setState(({ notificationSet }) => {
const newNotificationSet = new Set(notificationSet);
newNotificationSet.delete(uid);
return {
notificationSet: newNotificationSet
};
});
};
}
You don't need to do new Set() every time because you already initialize the state with new Set() so now you just do as follow:
state = {notificationSet: new Set()}
updateNotificationCount = (uid, isNewMatch) => {
let notificationSet;
if (isNewMatch) {
notificationSet=this.state.notificationSet;
notificationSet.add(uid);
this.setState({
notificationSet: notificationSet
});
} else {
notificationSet=this.state.notificationSet;
notificationSet.delete(uid);
this.setState({
notificationSet : notificationSet
});
};
}

Crash with simple history push

just trying come silly stuff and playing around with Cycle.js. and running into problem. Basically I just have a button. When you click it it's suppose to navigate the location to a random hash and display it. Almost like a stupid router w/o predefined routes. Ie. routes are dynamic. Again this isn't anything practical I am just messing with some stuff and trying to learn Cycle.js. But the code below crashes after I click "Add" button. However the location is updated. If I actually just navigate to "#/asdf" it displays the correct content with "Hash: #/asdf". Not sure why the flow is crashing with error:
render-dom.js:242 TypeError: Cannot read property 'subscribe' of undefined(…)
import Rx from 'rx';
import Cycle from '#cycle/core';
import { div, p, button, makeDOMDriver } from '#cycle/dom';
import { createHashHistory } from 'history';
import ranomdstring from 'randomstring';
const history = createHashHistory({ queryKey: false });
function CreateButton({ DOM }) {
const create$ = DOM.select('.create-button').events('click')
.map(() => {
return ranomdstring.generate(10);
}).startWith(null);
const vtree$ = create$.map(rs => rs ?
history.push(`/${rs}`) :
button('.create-button .btn .btn-default', 'Add')
);
return { DOM: vtree$ };
}
function main(sources) {
const hash = location.hash;
const DOM = sources.DOM;
const vtree$ = hash ?
Rx.Observable.of(
div([
p(`Hash: ${hash}`)
])
) :
CreateButton({ DOM }).DOM;
return {
DOM: vtree$
};
}
Cycle.run(main, {
DOM: makeDOMDriver('#main-container')
});
Thank you for the help
I would further suggest using #cycle/history to do your route changing
(Only showing relevant parts)
import {makeHistoryDriver} from '#cycle/history'
import {createHashHistory} from 'history'
function main(sources) {
...
return {history: Rx.Observable.just('/some/route') } // a stream of urls
}
const history = createHashHistory({ queryKey: false })
Cycle.run(main, {
DOM: makeDOMDriver('#main-container'),
history: makeHistoryDriver(history),
})
On your function CreateButton you are mapping your clicks to history.push() instead of mapping it to a vtree which causes the error:
function CreateButton({ DOM }) {
...
const vtree$ = create$.map(rs => rs
? history.push(`/${rs}`) // <-- not a vtree
: button('.create-button .btn .btn-default', 'Add')
);
...
}
Instead you could use the do operator to perform the hashchange:
function CreateButton({ DOM }) {
const create$ =
...
.do(history.push(`/${rs}`)); // <-- here
const vtree$ = Observable.of(
button('.create-button .btn .btn-default', 'Add')
);
...
}
However in functional programming you should not perform side effects on you app logic, every function must remain pure. Instead, all side effects should be handled by drivers. To learn more take a look at the drivers section on Cycle's documentation
To see a working driver jump at the end of the message.
Moreover on your main function you were not using streams to render your vtree. It would have not been reactive to locationHash changes because vtree$ = hash ? ... : ... is only evaluated once on app bootstrapping (when the main function is evaluated and "wires" every streams together).
An improvement will be to declare your main's vtree$ as following while keeping the same logic:
const vtree$ = hash$.map((hash) => hash ? ... : ...)
Here is a complete solution with a small locationHash driver:
import Rx from 'rx';
import Cycle from '#cycle/core';
import { div, p, button, makeDOMDriver } from '#cycle/dom';
import { createHashHistory } from 'history';
import randomstring from 'randomstring';
function makeLocationHashDriver (params) {
const history = createHashHistory(params);
return (routeChange$) => {
routeChange$
.filter(hash => {
const currentHash = location.hash.replace(/^#?\//g, '')
return hash && hash !== currentHash
})
.subscribe(hash => history.push(`/${hash}`));
return Rx.Observable.fromEvent(window, 'hashchange')
.startWith({})
.map(_ => location.hash);
}
}
function CreateButton({ DOM }) {
const create$ = DOM.select('.create-button').events('click')
.map(() => randomstring.generate(10))
.startWith(null);
const vtree$ = Rx.Observable.of(
button('.create-button .btn .btn-default', 'Add')
);
return { DOM: vtree$, routeChange$: create$ };
}
function main({ DOM, hash }) {
const button = CreateButton({ DOM })
const vtree$ = hash.map(hash => hash
? Rx.Observable.of(
div([
p(`Hash: ${hash}`)
])
)
: button.DOM
)
return {
DOM: vtree$,
hash: button.routeChange$
};
}
Cycle.run(main, {
DOM: makeDOMDriver('#main-container'),
hash: makeLocationHashDriver({ queryKey: false })
});
PS: there is a typo in your randomstring function name, I fixed it in my example.