How Amplifer uses Logux - a tool for communication between client and server

Logux - a tool for communication between client and server


My name is Vitaliy Rizo, I am a senior front-end developer at Amplifer. I’ll share how we use Logux in a web application: we organize real-time data exchange, error notifications without reloading the page, communication between browser tabs and integration with Redux.


Amplifer is a service for publishing on social networks. It was necessary to quickly and reliably notify users of errors without reloading the page: if suddenly it was not possible to process the image, the VKontakte API fell, or Facebook decided again not to post. Looking ahead, I’ll say that we also planned to use Logux to discuss publications and rewrite the cross-posting popup from RSS. But first, about why we were not satisfied with the available solutions.


Possible solutions: WebSocket, Firebase and Swarm.js


Usually, WebSocket is used to implement auto-update information. With it, you do not need to send requests for information every second, as you would have to do with traditional HTTP requests, and the header size will be small. However, WebSocket has disadvantages:


  • . , , , . , : , ;
  • , . ;
  • , , ;
  • , (CRDT), .

Firebase, , — . Firebase CRDT, Redux . CRDT Swarm.js, Redux , .


Logux


, — Logux. , Redux, CRDT . - , API-: , .


, , « ». :


imports-api.js


update (project, id, data) {
  return put(project, `settings/imports/${ id }`, convert(data))
}

imports.js


onUpdate (projectId, importId, changed) {
  return dispatch({
    projectId,
    importId,
    import: changed,
    type: 'amplifr/imports/update'
  })
}

:


imports.js


let dispatch = useDispatch()
let onUpdate = (projectId, importId, changed) => dispatch.sync({
  projectId,
  importId,
  import: changed,
  type: 'amplifr/imports/update'
}, { channels: ['imports/' + projectId] })

sync dispatch, , , . , Logux .


Logux


New error section



— . , . , .


, Logux-. .


Logux Redux. Redux createStore. Logux, «» createLoguxCreator:


import { createLoguxCreator } from '@logux/redux'

let createStore = createLoguxCreator({
  credentials: loguxToken,
  subprotocol: '0.6.5',
  userId,
  server: 'wss://logux.amplifr.com'
})

let store = createStore(reducers)

store.client.start()

createLoguxCreator :


  1. . , . gon;
  2. Logux-;
  3. . , , .

Logux- , . , - posts/1, . , WebSocket.


? — , , posts/1. subscribe — :


import useSubscription from '@logux/redux/use-subscription'

let Notices = props => {
  let isSubscribing = useSubscription([`projects/${ props.id }`])

  if (isSubscribing) {
    return <Loader />
  } else {
    // Render Notices
  }
}

— , HTTP- Redux- Logux, Logux- :


def schedule_logux
  LoguxNotificationWorker.perform_async(
    { type: 'amplifr/notices/add', notice: Logux::NoticePresenter.new(notice).to_json },
    channels: ["projects/#{project.id}"]
  )
end

, . - , , . , :


import { useDispatch } from 'react-redux'

let dispatch = useDispatch()
let onNoticeHide = noticeId => dispatch.sync({
  type: 'amplifr/notices/hide',
  noticeId
}, {
  channels: [`projects/${ projectId }`]
})

, , Logux:


  • . ;
  • , ;
  • , (). -;
  • , , . , .


Amplifier due to an error in the error section


-


Logux , . , - , . , . , , , .


Logux . «» , , localStorage. , Firefox Safari localStorage! Logux , , . , .


, , Logux , , . , , .


, Logux, .


Logux


Discussion of publications in Amplifer


, Logux, . — , :


let PostEditor = { isApprovable, postId, … } => {
  let isSubscribing = useSubscription(isApprovable ? [`posts/${ postId }`] : [])

  if (isSubscribing) {
    return <Loader />
  } else {
    // Render PostEditor
  }
}

notes :


import { useDispatch, useSelector } from 'react-redux'

let dispatch = useDispatch()
let notes = useSelector(notes.filter(note => note.postId === postId))
let onNoteAdd = note => dispatch.sync({
  type: 'amplifr/notes/add',
  note
}, {
  channels: [`posts/${ postId }`]
})

. , :


export default function notes (state = [], action) {
  if (action.type === 'amplifr/notes/add') {
    return state.concat([action.note])
  } else if (action.type === 'amplifr/posts/close') {
    return state.filter(i => i.postId !== action.postId)
  } else {
    return state
  }
}

. Logux, .



Eternal editor download


, @subscribe. , isSubscribing: true. , , . .


, . , squid 3, WebSocket ( Logux WebSocket). , , . — .


RSS Logux


, Logux AJAX-. , «» «» Firefox.


AJAX Logux RSS. RSS- . RSS-, .


Cross-Hosting Settings



. :


import { useDispatch } from 'react-redux'

let dispatch = useDispatch()
let onCreate = (projectId, importId, import) => {
  return dispatch.sync({
    importId,
    import,
    type: 'amplifr/imports/add'
  }, { channels: ['imports/' + projectId] })
}

let onUpdate = (projectId, importId, changed) => {
  return dispatch.sync({
    importId,
    changed,
    type: 'amplifr/imports/update'
  }, { channels: ['imports/' + projectId] })
}

, , , Logux Optimitstic UI — . , , . - , .


Optimistic Interface Example


«» .


«», , RSS- . , . Logux (dispatch.sync(…).catch(…)), . -, , .


: catch() JSON, try { JSON.parse(…) } catch { … }. .


Logux ?


Logux WebSocket, , SPA . , . , , :


import status from '@logux/client/status'

let connected = false

status(store.client, state => {
  if (state === 'synchronized') connected = true
}

setTimeout(() => {
  if (!connected) {
    sentry.track(new Error('No Logux connection for 10 minutes'))
  }
}, 60 * 1000)

100 , . , - , :


Logux Problem Finding Algorithm


, WebSocket: -, , , , , AdBlocker Kaspersky Protection. , , , Logux, .


Logux ,


Logux , . , , . RSS-, , . - , , , , .


, . — Logux. -. , , , , :


import log from '@logux/client/log'

let store = createStore(reducers)

log(store.client)

window.store = store

:


  1. RSS- ;
  2. , ;
  3. RSS-, ;
  4. ;
  5. !

:


window.store.client.log.store.created.filter(i => i[0].type === 'amplifr/popups/set')

, - : meta.tab undefined. , . , client.id client.tabId @logux/redux id tabId. , , Logux , , .


, - «» Logux, :


Logux : «? , , ?». , — , , Redux. « », , . Logux .


:


  1. Logux, Redux, CRDT ;
  2. C Logux , ;
  3. Logux , ;
  4. Logux has its drawbacks: the system is not simple, there are still bugs and solutions are not always found quickly;
  5. In Amplifer, the advantages of Logux outweighed the cons. We plan to continue to use it when implementing suitable projects.


⌘⌘⌘


I hope Logux finds use in your project. If you have any questions, please write to me on Twitter or by mail .


Thanks to Alexander Marfitsin and Andrei Sitnik for their help in preparing this article.


All Articles