-
Notifications
You must be signed in to change notification settings - Fork 15
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GG this looks great. I can't even remember all of the little comments I left as I went but maybe some of them will be helpful!
/* public */ | ||
|
||
get rooms () { | ||
return values(this.roomStore.snapshot()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[room|user]store.snapshot
is schweeeeet ❤️
src/chat-manager.js
Outdated
}) | ||
return Promise.all([ | ||
currentUser.establishUserSubscription(hooks), | ||
currentUser.establishPresenceSubscription(hooks) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably with the way it's set up at the moment, if the presence subscription fails then the promise returned from the call to connect
will end up in the catch
?
I think this is how I have it in the Swift one at the moment as well, but I'm not completely sure about it. I think it makes the most sense, but more so when we have the ability to disable presence and cursors, if a user so wishes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good point, if either fails we'll end up in the catch. This is less of an issue when you can disable presence, but even then... if someone has everything on by default, would it be better to go on even if the presence subscription fails on the off chance the client isn't using any of the presence stuff? If they are it would break stuff later on, but maybe partial functionality is better than no functionality?
It wouldn't be a big change, we could just have establishPresenceSubscription
always resolve, and the presence stuff would be undefined
and users could handle that however they want... 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm loath to add more config but maybe we could have something akin to a strict
mode, which is false
by default, but it can be set to true
if someone wants the behaviour of "if any of the initial connection requests fail then I want the catch
block to be hit of the connection promise".
Then we can have the default being that as long as the user subscription succeeds, we resolve the promise. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be a good compromise yeah. I'll make a card to do the opting out of presence / cursors etc stuff and include this as part of it. We don't necessarily even have to document it but it would be nice to have if customers were like "wth everything broke horribly because the presence sub failed". That being said... if we're not going to document it, maybe we should just add it later if someone actually runs in to that problem. hmmmm
} | ||
}) | ||
.then(res => { | ||
const basicRoom = parseBasicRoom(JSON.parse(res)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a basic room and not a full room? Maybe this gets handled by adding a basic room to the room store - I've not gotten to that yet though!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the this.roomStore.set(basicRoom.id, basicRoom)
below returns a full Room
:)
src/current-user.js
Outdated
return this.apiInstance | ||
.request({ | ||
method: 'GET', | ||
path: `/users/${encodeURIComponent(this.id)}/rooms?joinable=true` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
encodeURIComponent
could probably just be called once at the point of initialisation to save doing it every time there's the id
appearing in a path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good shout 👍
src/current-user.js
Outdated
}) | ||
.then(() => this.roomStore.pop(roomId)) | ||
.catch(err => { | ||
this.logger.warn(`error joining room ${roomId}:`, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
joining -> leaving
) | ||
return this.userStore.fetchMissingUsers( | ||
uniq(map(prop('senderId'), messages)) | ||
).then(() => sort((x, y) => x.id - y.id, messages)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to figure out what's going on here - it looks clever, but haven't worked it out yet 😛
src/current-user.js
Outdated
typeCheckObj('hooks', 'function', hooks) | ||
messageLimit && typeCheck('messageLimit', 'number', messageLimit) | ||
// TODO what is the desired behaviour if there is already a subscription to | ||
// this room? Close the old one? Throw an error? Merge the hooks? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent question - I think the easiest thing for us would be to close the existing subscription and just replace the whole thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems like a good solution, in that case this depends on proper subscription cancelling and then it should be easy. I'll put it on the list!
src/message.js
Outdated
this.text = basicMessage.text | ||
this.createdAt = basicMessage.createdAt | ||
this.updatedAt = basicMessage.updatedAt | ||
this.userStore = userStore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if the Message
having a room and user store is genius or madness. Probably simplifies the "enrichment" process a lot, but then I can imagine there being a lot of getSync
calls happening which makes me squirm a bit. Although I suppose that has to happen whether you get the sender or room later or at the point of message creation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the reasoning was that I wanted to keep all the mutable state in the three "stores", and then everything else is always kept up to date for free. I don't think it should be too much of a performance hit, since getSync
is O(1)
, but something to try if things get slow though for sure :)
pendingGets = [] // [{ key, resolve }] | ||
|
||
initialize = initialStore => { | ||
this.store = clone(initialStore) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why clone and not store the initialStore
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm. The resoning when I wrote this was probably something like: if the consumer mutates initialStore
after initialization then that could fuck everything up, buuuut since we only do all the initialization intenally we probably needn't bother.
src/utils.js
Outdated
// appendQueryParam :: String -> String -> String -> String | ||
export const appendQueryParam = (key, value, url) => { | ||
const [ before, after ] = split('?', url) | ||
return before + '?' + (after ? after + '&' : '') + urlEncode({ [key]: value }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/current-user.js
Outdated
} | ||
|
||
/* internal */ | ||
|
||
uploadDataAttachment = (roomId, { file, name }) => { | ||
// TODO some validation on allowed file names? | ||
// TODO polyfill FormData? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll have to do this at some point (or use lower level methods), but the existing JS sdk uses FormData
as well, and the current goal is to match functionality with what already exists asap so that we can get ths released.
+1 for renaming delegate to hooks. 'Delegate' is popular in the Obj-C/Swift scene, but not as intuitive in the JS world. |
4652283
to
3564f6f
Compare
.then(() => t.end()) | ||
.catch(endWithErr(t)) | ||
t.timeoutAfter(TEST_TIMEOUT) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we missing a case for data attachments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mdpye yes! my bad, going to add one now. (will cover fetchAttachment
too)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Do we need to ship with |
I'm tempted to keep them because I can see it being something that an admin-type user might want to be able to do from the client. Is there a reason to remove them other than them requiring permissions elevated from the default? |
nah not at all. Just trying to narrow down the list of "final bits and bobs" to do before releasing this. It's a short job to add them :) |
updateRoom & deleteRoom changes LGTM 👍 |
a1eb87a
to
0992758
Compare
@callum-oakley pretty random but purely out of curiosity, what was the reason to move away from typescript internally? :) Cheers 🍺 |
replied in slack :) |
The majority of the functionality of the typescript library has now been translated to vanilla js and refactored along the way. Probably worth getting some feedback on this as it is before committing to the finishing touches.
API changes:
chatManager.connect
doesn't resolve with the current user object until the full initial state is known (the initial user request has already completed, presence subscription has been established etc)room
but now aroomId
"See luke's demo modified to use this branch: lukejacksonn/react-slack-clone#1
Internal changes:
Tests
The integration tests that currently exist were super useful for doing the rewrite, and will continue to be useful to know we haven't broken stuff. But they aren't very good at testing failure paths, and won't scale much more (they already take >60s to run!). So we might want to unit test things too? I was loathed to do this immediately, but it might make sense once we've more or less settled on a good internal structure.
Remaining work
make sure the error handling is watertight (it certainly isn't at the moment)still not, but probably "good enough for now"worker buildsBuild
yarn lint:build
Run the tests
yarn lint:build:test