From 493d576e4d6b9b44ff825b80bdc5d8ed0d64fc03 Mon Sep 17 00:00:00 2001 From: Andres Vargas Date: Sat, 28 Nov 2020 22:31:26 -0600 Subject: [PATCH] adding turbolinks and js for chat --- core/javascript/chat.js | 10 ---- .../javascript/controllers/chat_controller.js | 55 ++++++++++++++++--- core/javascript/example.js | 14 ++++- core/reflexes/book_search_reflex.py | 2 +- core/templates/_chat_demo.html | 20 ++++++- core/templates/chat.html | 12 ---- core/views/book_search.py | 10 +--- core/views/chat.py | 4 +- core/views/example.py | 10 +--- core/views/mixins.py | 43 +++++++++++---- package.json | 5 ++ yarn.lock | 15 +++++ 12 files changed, 135 insertions(+), 65 deletions(-) delete mode 100644 core/javascript/chat.js delete mode 100644 core/templates/chat.html diff --git a/core/javascript/chat.js b/core/javascript/chat.js deleted file mode 100644 index 82f77e3..0000000 --- a/core/javascript/chat.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Application } from 'stimulus' -import StimulusReflex from 'stimulus_reflex' -import WebsocketConsumer from 'sockpuppet-js' -import ChatController from './controllers/chat_controller' - -const application = Application.start() -const consumer = new WebsocketConsumer('ws://localhost:8000/ws/sockpuppet-sync') - -application.register("chat", ChatController) -StimulusReflex.initialize(application, { consumer }) diff --git a/core/javascript/controllers/chat_controller.js b/core/javascript/controllers/chat_controller.js index f7dd935..2b488f4 100644 --- a/core/javascript/controllers/chat_controller.js +++ b/core/javascript/controllers/chat_controller.js @@ -1,14 +1,51 @@ -import { Controller } from 'stimulus'; -import StimulusReflex from 'stimulus_reflex'; +import Rails from '@rails/ujs' +import { debounce } from 'lodash-es' +import ApplicationController from './application_controller' -export default class extends Controller { - connect() { - StimulusReflex.register(this) +let lastMessageId +const reload = controller => { + controller.stimulate('ChatReflex#reload') +} +const debouncedReload = debounce(reload, 100) + +export default class extends ApplicationController { + static get targets () { + return ['list', 'input'] + } + + connect () { + super.connect() + this.scroll(100) + } + + post (event) { + Rails.stopEverything(event) + lastMessageId = Math.random() + this.stimulate( + 'ChatReflex#post', + this.element.dataset.color, + this.inputTarget.value, + lastMessageId + ) } - increment(event) { - console.log('increment') - event.preventDefault() - this.stimulate('ChatReflex#increment', 1) + afterPost () { + this.inputTarget.value = '' + this.inputTarget.focus() + this.scroll(1) + } + + scroll (delay = 10) { + const lists = document.querySelectorAll('[data-target="chat.list"]') + setTimeout(() => { + lists.forEach(e => (e.scrollTop = e.scrollHeight)) + }, delay) + } + + reload (event) { + const { messageId } = event.detail + if (messageId === lastMessageId) return + debouncedReload(this) } } + diff --git a/core/javascript/example.js b/core/javascript/example.js index 1c72462..6d732b2 100644 --- a/core/javascript/example.js +++ b/core/javascript/example.js @@ -1,20 +1,28 @@ import { Application } from 'stimulus' import StimulusReflex from 'stimulus_reflex' import WebsocketConsumer from 'sockpuppet-js' - +import CableReady from 'cable_ready' import debounced from 'debounced' import BookSearchController from './controllers/book_search_controller' import ExampleController from './controllers/example_controller' +import ChatController from './controllers/chat_controller' debounced.initialize() -//import TurboLinks from 'turbolinks' +import TurboLinks from 'turbolinks' -//TurboLinks.start() +TurboLinks.start() const application = Application.start() const ssl = location.protocol !== 'https:' ? '' : 's'; const consumer = new WebsocketConsumer(`ws${ssl}://${location.hostname}:${location.port}/ws/sockpuppet-sync`) +consumer.subscriptions.create('ChatChannel', { + received (data) { + if (data.cableReady) CableReady.perform(data.operations) + } +}) + application.register("example", ExampleController) application.register("book-search", BookSearchController) +application.register("chat", ChatController) StimulusReflex.initialize(application, { consumer, debug: true }) diff --git a/core/reflexes/book_search_reflex.py b/core/reflexes/book_search_reflex.py index 0b448dd..f8bacdb 100644 --- a/core/reflexes/book_search_reflex.py +++ b/core/reflexes/book_search_reflex.py @@ -4,7 +4,7 @@ class BookSearchReflex(Reflex): def perform(self, query=""): - resp = requests.get('http://openlibrary.org/search.json', params={'q':query}) + resp = requests.get("http://openlibrary.org/search.json", params={"q": query}) resp.raise_for_status() books = resp.json() self.books = books.get("docs", []) diff --git a/core/templates/_chat_demo.html b/core/templates/_chat_demo.html index d792f7b..cc40fef 100644 --- a/core/templates/_chat_demo.html +++ b/core/templates/_chat_demo.html @@ -1 +1,19 @@ -chat demo + +
+ + {% for chat in chats %} + + {% endfor %} + +
+ + + +
+
diff --git a/core/templates/chat.html b/core/templates/chat.html deleted file mode 100644 index b363e6e..0000000 --- a/core/templates/chat.html +++ /dev/null @@ -1,12 +0,0 @@ - -{% load static %} - - - - - Increment {{ count }} - diff --git a/core/views/book_search.py b/core/views/book_search.py index e7ba2c3..f61dd85 100644 --- a/core/views/book_search.py +++ b/core/views/book_search.py @@ -1,15 +1,9 @@ from django.views.generic.base import TemplateView -from .mixins import MixinBase +from .mixins import BookSearchMixin -class BookSearch(MixinBase, TemplateView): +class BookSearch(BookSearchMixin, TemplateView): demo_template = "_book_search_demo.html" subtitle = 'Search Book' - files = ( - ('core/reflexes/book_search_reflex.py', 'python', 'python3'), - ('core/views/book_search.py', 'python', 'python3'), - ('core/javascript/controllers/book_search_controller.js', 'javascript', 'javascript'), - ('core/templates/_book_search_demo.html', 'html', 'htmldjango'), - ) book_search = BookSearch.as_view() diff --git a/core/views/chat.py b/core/views/chat.py index 580de5b..aa1f096 100644 --- a/core/views/chat.py +++ b/core/views/chat.py @@ -3,7 +3,7 @@ class ChatView(MixinBase, TemplateView): demo_template = '_chat_demo.html' - subtitle = 'Increment' + subtitle = 'Chat' files = ( ('core/views/chat.py', 'python', 'python3'), ('core/reflexes/chat_reflex.py', 'python', 'python3'), @@ -13,7 +13,7 @@ class ChatView(MixinBase, TemplateView): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context['count'] = self.request.session.get("count", 0) + context['chats'] = [dict(message='message1')] return context chat = ChatView.as_view() diff --git a/core/views/example.py b/core/views/example.py index 6825b3e..4089b45 100644 --- a/core/views/example.py +++ b/core/views/example.py @@ -1,15 +1,9 @@ from django.views.generic.base import TemplateView -from .mixins import MixinBase +from .mixins import ExampleMixin -class ExampleView(MixinBase, TemplateView): +class ExampleView(ExampleMixin, TemplateView): demo_template = '_example_demo.html' subtitle = 'Increment' - files = ( - ('core/views/example.py', 'python', 'python3'), - ('core/reflexes/example_reflex.py', 'python', 'python3'), - ('core/javascript/controllers/example_controller.js', 'javascript', 'javascript'), - ('core/templates/_example_demo.html', 'html', 'htmldjango'), - ) def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) diff --git a/core/views/mixins.py b/core/views/mixins.py index 17eb9a1..5a5a03c 100644 --- a/core/views/mixins.py +++ b/core/views/mixins.py @@ -6,27 +6,48 @@ class MixinBase: - template_name="demo.html" + template_name = "demo.html" demo_template = None subtitle = None def get_files(self): files = defaultdict(list) path_ = lambda x: open(os.path.join(BASE_PATH, x)).read() - for filename, filetype, pygment_type in self.files: + for filename, filetype, pygment_type in self.files: filesrc = path_(filename) - files[filetype].append({ - 'src': filesrc, - 'pygment_type': pygment_type, - 'filename': filename, - 'loc': len(filesrc.split('\n')) - }) + files[filetype].append( + { + "src": filesrc, + "pygment_type": pygment_type, + "filename": filename, + "loc": len(filesrc.split("\n")), + } + ) return dict(files) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['files'] = self.get_files() - context['demo_template'] = self.demo_template - context['subtitle'] = self.subtitle + context["files"] = self.get_files() + context["demo_template"] = self.demo_template + context["subtitle"] = self.subtitle return context + +class BookSearchMixin(MixinBase): + files = ( + ("core/reflexes/book_search_reflex.py", "python", "python3"), + ("core/views/book_search.py", "python", "python3"), + ("core/javascript/controllers/book_search_controller.js", "javascript", + "javascript",), + ("core/templates/_book_search_demo.html", "html", "htmldjango"), + ) + + +class ExampleMixin(MixinBase): + files = ( + ('core/views/example.py', 'python', 'python3'), + ('core/reflexes/example_reflex.py', 'python', 'python3'), + ('core/javascript/controllers/example_controller.js', 'javascript', 'javascript'), + ('core/templates/_example_demo.html', 'html', 'htmldjango'), + ) + diff --git a/package.json b/package.json index 953f1d1..1192c24 100644 --- a/package.json +++ b/package.json @@ -19,5 +19,10 @@ "stimulus_reflex": "^3.4.0-pre5", "webpack": "^5.6.0", "webpack-cli": "^4.2.0" + }, + "dependencies": { + "@rails/ujs": "^6.0.3-4", + "lodash-es": "^4.17.15", + "turbolinks": "^5.2.0" } } diff --git a/yarn.lock b/yarn.lock index 016d5dd..6f59fe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.3.tgz#722b4b639936129307ddbab3a390f6bcacf3e7bc" integrity sha512-I01hgqxxnOgOtJTGlq0ZsGJYiTEEiSGVEGQn3vimZSqEP1HqzyFNbzGTq14Xdyeow2yGJjygjoFF1pmtE+SQaw== +"@rails/ujs@^6.0.3-4": + version "6.0.3-4" + resolved "https://nexus.gcds.coke.com/repository/npm-group/@rails/ujs/-/ujs-6.0.3-4.tgz#8dafc84178080f9c4f21076953ea1d0dc0bfe0fc" + integrity sha512-pNEEndJYNMCYEZG79MkoMc40AYKBfm0md8pawJ/SUu/1aIhToJcKu+9hHT/7WMLudsakOgC/C8KKFuZOs4QTgw== + "@stimulus/core@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@stimulus/core/-/core-1.1.1.tgz#42b0cfe5b73ca492f41de64b77a03980bae92c82" @@ -639,6 +644,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash-es@^4.17.15: + version "4.17.15" + resolved "https://nexus.gcds.coke.com/repository/npm-group/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + lodash@^4.17.15: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" @@ -976,6 +986,11 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +turbolinks@^5.2.0: + version "5.2.0" + resolved "https://nexus.gcds.coke.com/repository/npm-group/turbolinks/-/turbolinks-5.2.0.tgz#e6877a55ea5c1cb3bb225f0a4ae303d6d32ff77c" + integrity sha512-pMiez3tyBo6uRHFNNZoYMmrES/IaGgMhQQM+VFF36keryjb5ms0XkVpmKHkfW/4Vy96qiGW3K9bz0tF5sK9bBw== + typical@^5.0.0, typical@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"