|
| 1 | +import { createElement, nop } from "../lib/skeleton/index.js"; |
| 2 | +import { qs, safe } from "../lib/dom.js"; |
| 3 | +import rxjs, { effect, applyMutation, preventDefault } from "../../lib/rx.js"; |
| 4 | +import ajax from "../../lib/ajax.js"; |
| 5 | +import t from "../locales/index.js"; |
| 6 | +import { createModal, MODAL_RIGHT_BUTTON } from "../components/modal.js"; |
| 7 | + |
| 8 | +export default async function(render, { path }) { |
| 9 | + const $page = createElement(` |
| 10 | + <div> |
| 11 | + <h3 class="no-select"> |
| 12 | + <img style="position:relative;top:1px;" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIHN0eWxlPSJzdHJva2U6IzU3NTk1YSIgZD0iTTIxIDE1YTIgMiAwIDAgMS0yIDJIN2wtNCA0VjVhMiAyIDAgMCAxIDItMmgxNGEyIDIgMCAwIDEgMiAyeiIvPjwvc3ZnPg==" alt="chat"> |
| 13 | + <span data-bind="title">${t("Chat")}</span> |
| 14 | + </h3> |
| 15 | + <ul data-bind="messages"></ul> |
| 16 | + <style>${CSS}</style> |
| 17 | + </div> |
| 18 | + `); |
| 19 | + render($page); |
| 20 | + |
| 21 | + const refresh$ = getMessages(path).pipe( |
| 22 | + rxjs.map((messages) => { |
| 23 | + const $messages = document.createDocumentFragment(); |
| 24 | + for (const message of messages) { |
| 25 | + $messages.appendChild(renderMessage(message, { |
| 26 | + onClick: () => onMessageClick({ path: message.path }), |
| 27 | + })); |
| 28 | + } |
| 29 | + return $messages; |
| 30 | + }), |
| 31 | + applyMutation(qs($page, `[data-bind="messages"]`), "replaceChildren"), |
| 32 | + ); |
| 33 | + |
| 34 | + effect(refresh$.pipe( |
| 35 | + )); |
| 36 | + |
| 37 | + effect(rxjs.of(createElement(`<form><input type="text" name="message" placeholder="${t("Chat")}" /></form>`)).pipe( |
| 38 | + applyMutation(qs($page, `[data-bind="title"]`), "replaceChildren"), |
| 39 | + rxjs.mergeMap(() => rxjs.fromEvent(qs($page, "form"), "submit")), |
| 40 | + preventDefault(), |
| 41 | + rxjs.mergeMap((e) => { |
| 42 | + const message = new FormData(e.target).get("message"); |
| 43 | + qs($page, "input").value = "..." |
| 44 | + return createMessage({ message, path }); |
| 45 | + }), |
| 46 | + rxjs.tap(() => qs($page, "input").value = ""), |
| 47 | + rxjs.mergeMap(() => refresh$), |
| 48 | + )); |
| 49 | +} |
| 50 | + |
| 51 | +function renderMessage(obj, { onClick = nop, sidebar = true }) { |
| 52 | + const $message = createElement(` |
| 53 | + <li title="${safe(obj.message)}"> |
| 54 | + <a data-link draggable="false"> |
| 55 | + <div class="${sidebar ? "ellipsis" : "" }"> |
| 56 | + <span class="message-author">${obj.author}:</span> |
| 57 | + <span class="message-content">${obj.message}</span> |
| 58 | + </div> |
| 59 | + </a> |
| 60 | + </li> |
| 61 | + `); |
| 62 | + |
| 63 | + effect(rxjs.fromEvent($message, "click").pipe( |
| 64 | + rxjs.tap(() => onClick()), |
| 65 | + )); |
| 66 | + |
| 67 | + effect(rxjs.of(null).pipe( |
| 68 | + rxjs.filter(() => document.body.classList.contains("touch-no")), |
| 69 | + rxjs.tap(() => $message.onmouseenter = () => { |
| 70 | + const $things = document.querySelectorAll(".component_thing"); |
| 71 | + $things.forEach(($thing) => { |
| 72 | + const thingpath = $thing.getAttribute("data-path"); |
| 73 | + if (obj.path.indexOf(thingpath) !== -1) $thing.classList.add("hover"); |
| 74 | + }); |
| 75 | + $message.onmouseleave = () => $things.forEach(($thing) => $thing.classList.remove("hover")); |
| 76 | + }), |
| 77 | + )); |
| 78 | + |
| 79 | + return $message; |
| 80 | +} |
| 81 | + |
| 82 | +function onMessageClick({ path }) { |
| 83 | + const modalHTML = ` |
| 84 | + <div data-bind="thread"> |
| 85 | + <component-icon name="loading"></component-icon> |
| 86 | + </div> |
| 87 | + `; |
| 88 | + const $modal = createElement(modalHTML); |
| 89 | + createModal({})($modal); |
| 90 | + |
| 91 | + effect(getMessages(path).pipe( |
| 92 | + rxjs.map((messages) => { |
| 93 | + const $page = createElement(` |
| 94 | + <div> |
| 95 | + <form> |
| 96 | + <input name="message" type="text" placeholder="Message"> |
| 97 | + </form> |
| 98 | + <ul data-bind="messages" class="${messages.length > 7 ? "scroll-y" : ""}"></ul> |
| 99 | + </div> |
| 100 | + `); |
| 101 | + const $messages = document.createDocumentFragment(); |
| 102 | + for (const message of messages) { |
| 103 | + $messages.appendChild(renderMessage(message, { sidebar: false })); |
| 104 | + } |
| 105 | + qs($page, `[data-bind="messages"]`).appendChild($messages); |
| 106 | + return $page; |
| 107 | + }), |
| 108 | + applyMutation($modal, "replaceChildren"), |
| 109 | + rxjs.mergeMap(($modal) => rxjs.fromEvent(qs($modal, "form"), "submit")), |
| 110 | + preventDefault(), |
| 111 | + rxjs.mergeMap((e) => { |
| 112 | + $modal.replaceChildren(createElement(modalHTML)); |
| 113 | + return createMessage({ |
| 114 | + message: new FormData(e.target).get("message"), |
| 115 | + path, |
| 116 | + }); |
| 117 | + }), |
| 118 | + rxjs.first(), |
| 119 | + )); |
| 120 | +} |
| 121 | + |
| 122 | +function getMessages(path = "/") { |
| 123 | + return ajax({ url: "api/messages?path="+path, responseType: "json" }).pipe( |
| 124 | + rxjs.map(({ responseJSON }) => responseJSON.results.reverse()), |
| 125 | + ); |
| 126 | +} |
| 127 | + |
| 128 | +function createMessage(body) { |
| 129 | + return ajax({ |
| 130 | + url: "api/messages", |
| 131 | + method: "POST", |
| 132 | + body, |
| 133 | + }); |
| 134 | +} |
| 135 | + |
| 136 | +const CSS = ` |
| 137 | +/* MESSAGES */ |
| 138 | +[data-bind="messages"] { |
| 139 | + font-size: 0.9rem; |
| 140 | +} |
| 141 | +[data-bind="messages"] .message-author { |
| 142 | + font-weight: 500; |
| 143 | + opacity: 0.5; |
| 144 | +} |
| 145 | +
|
| 146 | +/* SIDEBAR */ |
| 147 | +.component_filemanager_shell .component_sidebar [data-bind="chat"] a { |
| 148 | + cursor: pointer; |
| 149 | +} |
| 150 | +
|
| 151 | +/* MODAL */ |
| 152 | +component-modal [data-bind="thread"] component-icon[name="loading"] { |
| 153 | + display: block; |
| 154 | + height: 30px; |
| 155 | + text-align: center; |
| 156 | +} |
| 157 | +component-modal [data-bind="thread"] form input { |
| 158 | + font-size: 1rem; |
| 159 | + border: 2px solid var(--border); |
| 160 | + border-radius: 5px; |
| 161 | + padding: 5px 10px; |
| 162 | + color: rgba(0, 0, 0, 0.75); |
| 163 | + width: 100%; |
| 164 | + display: block; |
| 165 | + box-sizing: border-box; |
| 166 | +} |
| 167 | +component-modal [data-bind="thread"] [data-bind="messages"] { |
| 168 | + list-style-type: none; |
| 169 | + margin: 10px 0 0 0; |
| 170 | + padding: 0; |
| 171 | + max-height: 200px; |
| 172 | +} |
| 173 | +component-modal [data-bind="thread"] [data-bind="messages"] > li { |
| 174 | + line-height: 1rem; |
| 175 | + margin: 5px 5px; |
| 176 | + text-align: justify; |
| 177 | + text-transform: capitalize; |
| 178 | +} |
| 179 | +`; |
0 commit comments