cheetah-framework
Version:
Cheetah Framework JS used in all our applications
199 lines (176 loc) • 5.77 kB
JavaScript
import LaravelModel from '@cheetah/models/LaravelModel'
import config from '@cheetah/config'
import LockListener from '@cheetah/utils/locks/LocksListener'
class LockResource extends LockListener {
/**
* @param {LaravelModel|String} model model on which to lock or name of the resource
* @param {Number|String} [modelId] the model id, if the model is not a LaravelModel
* @param {Object} [Echo]
*/
constructor (model, modelId) {
if (model instanceof LaravelModel) {
super(model.constructor)
this.resourceId = model[model.constructor.idKey]
} else {
super(model)
this.resourceId = modelId
}
this.isLocked = true
this.users = null
this.lockingUserId = null
}
get channelName () {
return 'locks.' + this.resourceName + '.' + this.resourceId
}
get channel () {
return this._channel || (this._channel = window.Echo?.join(this.channelName))
}
startListening () {
return super.startListening()?.here(users => {
this.users = users
}).joining(user => {
this.users.push(user)
}).leaving(user => {
const index = this.users.indexOf(user)
if (index > -1) {
this.users.splice(index, 1)
}
}).listen('.Locks.RequestLock', message => {
if (message.lock.user_id === config.locks.getUserId(config.locks.getCurrentUser())) {
typeof this.lockRequested === 'function' && this.lockRequested(message)
}
}).listen('.Locks.ModelLocked', message => {
this.isLocked = message.lock.user_id !== config.locks.getUserId(config.locks.getCurrentUser())
this.lockingUserId = message.lock.user_id
}).listen('.Locks.ModelUnlocked', message => {
if (this.isLocked) {
const firstUser = this.users.filter(user => config.locks.getUserId(user.user) !== message.lock.user_id).sort((a, b) => a.timestamp - b.timestamp)[0]
if (firstUser && config.locks.getUserId(firstUser.user) === config.locks.getUserId(config.locks.getCurrentUser())) {
typeof this.joinIsPossible === 'function' && this.joinIsPossible(message)
}
}
})
}
getUser (userId) {
return this.users?.find(user => config.locks.getUserId(user.user) === userId)
}
get lockingUser () {
return this.getUser(this.lockingUserId)?.user
}
async forceKick () {
const { data } = await axios.get(route('model.lock.requestKick', { modelType: this.resourceName, modelId: this.resourceId }), { params: { force: true }})
return data.user_id === config.locks.getUserId(config.locks.getCurrentUser())
}
async askForKick (accepted, refused) {
const stopListeningForKick = () => {
this.channel?.stopListening('.client-AcceptKick')
this.channel?.stopListening('.client-CancelKick')
}
this.channel?.listenForWhisper('AcceptKick', message => {
stopListeningForKick()
if (message.user_id === config.locks.getUserId(config.locks.getCurrentUser())) {
accepted(message)
} else {
refused(message)
}
}).listenForWhisper('CancelKick', () => {
stopListeningForKick()
refused()
})
try {
const { data } = await axios.get(route('model.lock.requestKick', { modelType: this.resourceName, modelId: this.resourceId }))
// If you get the lock, unlock the resource
if (data.user_id === config.locks.getUserId(config.locks.getCurrentUser())) {
stopListeningForKick()
this.isLocked = false
return true
}
} catch (e) {
stopListeningForKick()
refused()
RemoteErrors(e)
}
}
async unlock () {
return await axios.delete(route('model.lock.destroy', { modelType: this.resourceName, modelId: this.resourceId }))
}
async acceptKick (userId, force = false) {
force ? this.unlock() : await this.unlock()
this.lockingUserId = userId
this.channel.whisper('AcceptKick', {
user_id: userId
})
this.refreshResource()
}
cancelKick () {
this.channel.whisper('CancelKick', {
user_id: this.userId
})
}
async tryLocking () {
try {
if (!this._listening) {
this.startListening()
}
const response = await axios.post(route('model.lock.store', { modelType: this.resourceName, modelId: this.resourceId }))
this.isLocked = false
return response
} catch (error) {
if (error.response?.status === 423) {
this.isLocked = true
this.lockingUserId = error.response.data.lock?.user_id
return false
}
RemoteErrors(error)
}
return !this.isLocked
}
refreshResource () {
if (typeof this.refresh === 'function') {
this.refresh()
return
}
setTimeout(window.location.reload, 2000)
}
/**
* @typedef Message
* @type {Object}
* @property {number|String} id the model id
* @property {String} type the model type
*/
/**
* @callback lockMessageCallback
* @param {Message} message
* @returns {LockResource}
*/
/**
* @param {lockMessageCallback} callback
* @returns {LockResource}
*/
setOnLockRequest (callback) {
/** @private */
this.lockRequested = callback
return this
}
/**
* @param {lockMessageCallback} callback
* @returns {LockResource}
*/
setOnJoinPossible (callback) {
/** @private */
this.joinIsPossible = callback
return this
}
/**
* Sets the function to call when we need to refresh the resource.
* By default, we refresh the page 2 seconds after notifying the user.
* @param {Function} callback The function to call for a refresh
* @returns {LockResource} this
*/
setRefreshResourceFunction (callback) {
/** @private */
this.refresh = callback
return this
}
}
export default LockResource