mirror of
https://github.com/ClaperCo/Claper.git
synced 2025-12-15 19:37:53 +01:00
472 lines
14 KiB
JavaScript
472 lines
14 KiB
JavaScript
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
|
||
// to get started and then uncomment the line below.
|
||
// import "./user_socket.js"
|
||
|
||
// You can include dependencies in two ways.
|
||
//
|
||
// The simplest option is to put them in assets/vendor and
|
||
// import them using relative paths:
|
||
//
|
||
// import "./vendor/some-package.js"
|
||
//
|
||
// Alternatively, you can `npm install some-package` and import
|
||
// them using a path starting with the package name:
|
||
//
|
||
// import "some-package"
|
||
//
|
||
|
||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||
import "phoenix_html"
|
||
// Establish Phoenix Socket and LiveView configuration.
|
||
import {Socket, Presence} from "phoenix"
|
||
import {LiveSocket} from "phoenix_live_view"
|
||
import topbar from "../vendor/topbar"
|
||
import Alpine from 'alpinejs'
|
||
import flatpickr from "flatpickr"
|
||
import moment from "moment-timezone"
|
||
import QRCodeStyling from "qr-code-styling"
|
||
import { Presenter } from "./presenter"
|
||
import { Manager } from "./manager"
|
||
window.moment = moment
|
||
|
||
|
||
window.moment.locale('fr', {
|
||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
|
||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
|
||
monthsParseExact : true,
|
||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
|
||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
|
||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
|
||
weekdaysParseExact : true,
|
||
longDateFormat : {
|
||
LT : 'HH:mm',
|
||
LTS : 'HH:mm:ss',
|
||
L : 'DD/MM/YYYY',
|
||
LL : 'D MMMM YYYY',
|
||
LLL : 'D MMMM YYYY HH:mm',
|
||
LLLL : 'dddd D MMMM YYYY HH:mm'
|
||
},
|
||
calendar : {
|
||
sameDay : '[Aujourd’hui à] LT',
|
||
nextDay : '[Demain à] LT',
|
||
nextWeek : 'dddd [à] LT',
|
||
lastDay : '[Hier à] LT',
|
||
lastWeek : 'dddd [dernier à] LT',
|
||
sameElse : 'L'
|
||
},
|
||
relativeTime : {
|
||
future : 'dans %s',
|
||
past : 'il y a %s',
|
||
s : 'quelques secondes',
|
||
m : 'une minute',
|
||
mm : '%d minutes',
|
||
h : 'une heure',
|
||
hh : '%d heures',
|
||
d : 'un jour',
|
||
dd : '%d jours',
|
||
M : 'un mois',
|
||
MM : '%d mois',
|
||
y : 'un an',
|
||
yy : '%d ans'
|
||
},
|
||
dayOfMonthOrdinalParse : /\d{1,2}(er|e)/,
|
||
ordinal : function (number) {
|
||
return number + (number === 1 ? 'er' : 'e');
|
||
},
|
||
meridiemParse : /PD|MD/,
|
||
isPM : function (input) {
|
||
return input.charAt(0) === 'M';
|
||
},
|
||
// In case the meridiem units are not separated around 12, then implement
|
||
// this function (look at locale/id.js for an example).
|
||
// meridiemHour : function (hour, meridiem) {
|
||
// return /* 0-23 hour, given meridiem token and hour 1-12 */ ;
|
||
// },
|
||
meridiem : function (hours, minutes, isLower) {
|
||
return hours < 12 ? 'PD' : 'MD';
|
||
},
|
||
week : {
|
||
dow : 1, // Monday is the first day of the week.
|
||
doy : 4 // Used to determine first week of the year.
|
||
}
|
||
});
|
||
window.moment.locale(navigator.languages[0].split('-')[0])
|
||
|
||
|
||
window.Alpine = Alpine
|
||
Alpine.start()
|
||
|
||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||
let Hooks = {}
|
||
|
||
Hooks.Scroll = {
|
||
mounted() {
|
||
if (this.el.dataset.postsNb > 4) window.scrollTo({top: document.querySelector(this.el.dataset.target).scrollHeight, behavior: 'smooth'});
|
||
this.handleEvent("scroll", () => {
|
||
let t = document.querySelector(this.el.dataset.target)
|
||
if (this.el.childElementCount > 4 && (window.scrollY + window.innerHeight >= t.offsetHeight - 100)) {
|
||
window.scrollTo({top: t.scrollHeight, behavior: 'smooth'});
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
Hooks.ScrollIntoDiv = {
|
||
mounted() {
|
||
let t = document.querySelector(this.el.dataset.target)
|
||
if (this.el.dataset.postsNb > 4) t.scrollTo({top: t.scrollHeight, behavior: 'smooth'});
|
||
|
||
this.handleEvent("scroll", () => {
|
||
let t = document.querySelector(this.el.dataset.target);
|
||
if (this.el.childElementCount > 4 && (t.scrollHeight - t.scrollTop < t.clientHeight + 100)) {
|
||
t.scrollTo({top: t.scrollHeight, behavior: 'smooth'});
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
Hooks.PostForm = {
|
||
onPress(e, submitBtn, TA) {
|
||
if (e.key == "Enter" && !e.shiftKey) {
|
||
e.preventDefault()
|
||
submitBtn.click()
|
||
} else {
|
||
if (TA.value.length > 2) {
|
||
submitBtn.classList.remove("opacity-50")
|
||
submitBtn.classList.add("opacity-100")
|
||
} else {
|
||
submitBtn.classList.add("opacity-50")
|
||
submitBtn.classList.remove("opacity-100")
|
||
}
|
||
}
|
||
},
|
||
onSubmit(e, TA) {
|
||
e.preventDefault()
|
||
document.getElementById("hiddenSubmit").click()
|
||
TA.value = ""
|
||
},
|
||
mounted() {
|
||
const submitBtn = document.getElementById("submitBtn")
|
||
const TA = document.getElementById("postFormTA")
|
||
submitBtn.addEventListener("click", (e) => this.onSubmit(e, TA))
|
||
TA.addEventListener("keydown", (e) => this.onPress(e, submitBtn, TA))
|
||
},
|
||
updated() {
|
||
const submitBtn = document.getElementById("submitBtn")
|
||
const TA = document.getElementById("postFormTA")
|
||
if (TA.value.length > 2) {
|
||
submitBtn.classList.remove("opacity-50")
|
||
submitBtn.classList.add("opacity-100")
|
||
} else {
|
||
submitBtn.classList.add("opacity-50")
|
||
submitBtn.classList.remove("opacity-100")
|
||
}
|
||
},
|
||
destroyed() {
|
||
const submitBtn = document.getElementById("submitBtn")
|
||
const TA = document.getElementById("postFormTA")
|
||
TA.removeEventListener("keydown", (e) => this.onPress(e, submitBtn, TA))
|
||
submitBtn.removeEventListener("click", (e) => this.onSubmit(e, TA))
|
||
}
|
||
}
|
||
|
||
Hooks.CalendarLocalDate = {
|
||
mounted() {
|
||
this.el.innerHTML = moment.utc(this.el.dataset.date).local().calendar()
|
||
},
|
||
updated() {
|
||
this.el.innerHTML = moment.utc(this.el.dataset.date).local().calendar()
|
||
}
|
||
}
|
||
Hooks.Pickr = {
|
||
mounted() {
|
||
const getDefaultDate = (dateStart, dateEnd, mode) => {
|
||
if (mode == "range") {
|
||
return moment.utc(dateStart).format('Y-MM-DD HH:mm') + " - " + moment.utc(dateEnd).format('Y-MM-DD HH:mm')
|
||
} else {
|
||
return moment.utc(dateStart).format('Y-MM-DD HH:mm')
|
||
}
|
||
};
|
||
this.pickr = flatpickr(this.el, {
|
||
wrap: true,
|
||
inline: false,
|
||
enableTime: true,
|
||
enable: JSON.parse(this.el.dataset.enable),
|
||
time_24hr: true,
|
||
formatDate: (date, format, locale) => {
|
||
return moment(date).utc().format('Y-MM-DD HH:mm');
|
||
},
|
||
parseDate: (datestr, format) => {
|
||
return moment.utc(datestr).local().toDate();
|
||
},
|
||
locale: {
|
||
firstDayOfWeek: 1,
|
||
rangeSeparator: ' - '
|
||
},
|
||
mode: this.el.dataset.mode == "range" ? "range" : "single",
|
||
minuteIncrement: 1,
|
||
dateFormat: "Y-m-d H:i",
|
||
defaultDate: getDefaultDate(this.el.dataset.defaultDateStart, this.el.dataset.defaultDateEnd, this.el.dataset.mode)
|
||
})
|
||
},
|
||
updated() {
|
||
},
|
||
destroyed() {
|
||
this.pickr.destroy()
|
||
}
|
||
}
|
||
Hooks.Presenter = {
|
||
mounted() {
|
||
this.presenter = new Presenter(this)
|
||
this.presenter.init()
|
||
}
|
||
}
|
||
Hooks.Manager = {
|
||
mounted() {
|
||
this.manager = new Manager(this)
|
||
this.manager.init()
|
||
},
|
||
updated() {
|
||
this.manager.update()
|
||
}
|
||
}
|
||
Hooks.OpenPresenter = {
|
||
open(e) {
|
||
e.preventDefault()
|
||
window.open(this.el.dataset.url, 'newwindow',
|
||
'width=' + window.screen.width + ',height=' + window.screen.height)
|
||
},
|
||
mounted() {
|
||
this.el.addEventListener("click", e => this.open(e))
|
||
},
|
||
updated() {
|
||
this.el.removeEventListener("click", e => this.open(e))
|
||
this.el.addEventListener("click", e => this.open(e))
|
||
},
|
||
destroyed() {
|
||
this.el.removeEventListener("click", e => this.open(e))
|
||
}
|
||
}
|
||
Hooks.GlobalReacts = {
|
||
mounted() {
|
||
|
||
this.handleEvent('global-react', data => {
|
||
var img = document.createElement("img");
|
||
img.src = "/images/icons/" + data.type + ".svg"
|
||
img.className = "react-animation absolute transform opacity-0" + this.el.className
|
||
this.el.appendChild(img)
|
||
})
|
||
this.handleEvent('reset-global-react', data => {
|
||
this.el.innerHTML = ""
|
||
})
|
||
}
|
||
}
|
||
Hooks.JoinEvent = {
|
||
mounted() {
|
||
const loading = document.getElementById("loading")
|
||
const submit = document.getElementById("submit")
|
||
const input = document.getElementById("input")
|
||
|
||
submit.addEventListener("click", (e) => {
|
||
if (input.value.length > 0) {
|
||
submit.style.display = "none"
|
||
loading.style.display = "block"
|
||
}
|
||
})
|
||
},
|
||
destroyed() {
|
||
const loading = document.getElementById("loading")
|
||
const submit = document.getElementById("submit")
|
||
const input = document.getElementById("input")
|
||
|
||
submit.removeEventListener("click", (e) => {
|
||
if (input.value.length > 0) {
|
||
submit.style.display = "none"
|
||
loading.style.display = "block"
|
||
}
|
||
})
|
||
}
|
||
}
|
||
Hooks.WelcomeEarly = {
|
||
mounted() {
|
||
|
||
if (localStorage.getItem("welcome-early") !== "false") {
|
||
this.el.style.display = "block"
|
||
this.el.children[0].addEventListener("click", (e) => {
|
||
e.preventDefault()
|
||
localStorage.setItem("welcome-early", "false")
|
||
this.el.style.display = "none"
|
||
})
|
||
}
|
||
|
||
},
|
||
destroyed() {
|
||
this.el.children[0].removeEventListener("click", (e) => {
|
||
e.preventDefault()
|
||
localStorage.setItem("welcome-early", "false")
|
||
this.el.style.display = "none"
|
||
})
|
||
}
|
||
}
|
||
Hooks.DefaultValue = {
|
||
mounted() {
|
||
this.el.value = moment(this.el.dataset.defaultValue ? this.el.dataset.defaultValue : undefined).utc().format();
|
||
}
|
||
}
|
||
Hooks.ClickFeedback = {
|
||
clicked(e) {
|
||
this.el.className = "animate__animated animate__rubberBand animate__faster";
|
||
setTimeout(() => {
|
||
this.el.className = "";
|
||
} , 500);
|
||
},
|
||
mounted() {
|
||
this.el.addEventListener("click", (e) => this.clicked(e))
|
||
},
|
||
destroy() {
|
||
this.el.removeEventListener("click", (e) => this.clicked(e))
|
||
}
|
||
}
|
||
Hooks.QRCode = {
|
||
draw() {
|
||
var url = this.el.dataset.code ? window.location.protocol + "//" + window.location.host + "/e/" + this.el.dataset.code : window.location.href;
|
||
this.el.style.width = document.documentElement.clientWidth * .27 + "px"
|
||
this.el.style.height = document.documentElement.clientWidth * .27 + "px"
|
||
|
||
if (this.qrCode == null) {
|
||
this.qrCode = new QRCodeStyling({
|
||
width: this.el.dataset.dynamic ? document.documentElement.clientWidth * .25 : 240,
|
||
height: this.el.dataset.dynamic ? document.documentElement.clientWidth * .25 : 240,
|
||
margin: 0,
|
||
image:
|
||
"/images/logo.png",
|
||
data: url,
|
||
cornersSquareOptions: {
|
||
type: "square"
|
||
},
|
||
dotsOptions: {
|
||
type: "square",
|
||
gradient: {
|
||
type: "linear",
|
||
rotation: Math.PI * 0.2,
|
||
colorStops: [{
|
||
offset: 0,
|
||
color: '#14bfdb'
|
||
}, {
|
||
offset: 1,
|
||
color: '#b80fef'
|
||
}]}
|
||
},
|
||
imageOptions: {
|
||
crossOrigin: "anonymous",
|
||
imageSize: 0.6,
|
||
margin: 10
|
||
}
|
||
})
|
||
this.qrCode.append(this.el)
|
||
} else {
|
||
this.qrCode.update({
|
||
width: this.el.dataset.dynamic ? document.documentElement.clientWidth * .25 : 240,
|
||
height: this.el.dataset.dynamic ? document.documentElement.clientWidth * .25 : 240
|
||
})
|
||
}
|
||
|
||
},
|
||
mounted() {
|
||
window.addEventListener("resize", this.draw.bind(this));
|
||
this.draw()
|
||
if (this.el.dataset.getUrl) {
|
||
setTimeout(() => {
|
||
var dataURL = this.qrCode._canvas.toDataURL()
|
||
document.getElementById("qr-url").value = dataURL
|
||
}, 500);
|
||
}
|
||
},
|
||
updated() {
|
||
},
|
||
destroyed() {
|
||
}
|
||
}
|
||
|
||
let Uploaders = {}
|
||
|
||
Uploaders.S3 = function(entries, onViewError){
|
||
entries.forEach(entry => {
|
||
let formData = new FormData()
|
||
let {url, fields} = entry.meta
|
||
Object.entries(fields).forEach(([key, val]) => formData.append(key, val))
|
||
formData.append("file", entry.file)
|
||
let xhr = new XMLHttpRequest()
|
||
onViewError(() => xhr.abort())
|
||
xhr.onload = () => xhr.status === 204 ? entry.progress(100) : entry.error()
|
||
xhr.onerror = () => entry.error()
|
||
xhr.upload.addEventListener("progress", (event) => {
|
||
if(event.lengthComputable){
|
||
let percent = Math.round((event.loaded / event.total) * 100)
|
||
if(percent < 100){ entry.progress(percent) }
|
||
}
|
||
})
|
||
|
||
xhr.open("POST", url, true)
|
||
xhr.send(formData)
|
||
})
|
||
}
|
||
|
||
|
||
let liveSocket = new LiveSocket("/live", Socket, {
|
||
uploaders: Uploaders,
|
||
params: {_csrf_token: csrfToken, tz: Intl.DateTimeFormat().resolvedOptions().timeZone},
|
||
hooks: Hooks,
|
||
dom: {
|
||
onBeforeElUpdated(from, to){
|
||
if(from._x_dataStack){
|
||
window.Alpine.clone(from, to)
|
||
window.Alpine.initTree(to)
|
||
}
|
||
}
|
||
},})
|
||
|
||
// Show progress bar on live navigation and form submits
|
||
let topBarScheduled = undefined
|
||
topbar.config({barColors: {0: "#fff"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
||
window.addEventListener("phx:page-loading-start", info => {
|
||
if(!topBarScheduled) {
|
||
topBarScheduled = setTimeout(() => topbar.show(), 500)
|
||
}
|
||
})
|
||
window.addEventListener("phx:page-loading-stop", info => {
|
||
clearTimeout(topBarScheduled)
|
||
topBarScheduled = undefined
|
||
topbar.hide()
|
||
})
|
||
|
||
const renderOnlineUsers = function(presences) {
|
||
let onlineUsers = Presence.list(presences, (_id, {metas: [user, ...rest]}) => {
|
||
return onlineUserTemplate(user);
|
||
}).join("")
|
||
|
||
document.querySelector("body").innerHTML = onlineUsers;
|
||
}
|
||
|
||
const onlineUserTemplate = function(user) {
|
||
return `
|
||
<div id="online-user">
|
||
<strong class="text-secondary">aaa</strong>
|
||
</div>
|
||
`
|
||
}
|
||
|
||
let presences = {};
|
||
liveSocket.on("presence_state", state => {
|
||
presences = Presence.syncState(presences, state)
|
||
renderOnlineUsers(presences)
|
||
})
|
||
|
||
// connect if there are any LiveViews on the page
|
||
liveSocket.connect()
|
||
|
||
// expose liveSocket on window for web console debug logs and latency simulation:
|
||
// >> liveSocket.enableDebug()
|
||
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
||
// >> liveSocket.disableLatencySim()
|
||
window.liveSocket = liveSocket |