chore(release): 0.2.5
[risinglegends.git] / src / client / htmx.ts
1 import { io } from 'socket.io-client';
2 import { authToken } from './http';
3 import { TimeManager } from '../shared/time';
4 import { configureChat } from './chat';
5
6 function $<T>(selector: string, root: any = document): T {
7   return root.querySelector(selector) as T;
8 }
9
10 function $$<T>(selector: string, root: any = document): T[] {
11   return Array.from(root.querySelectorAll(selector)) as T[];
12 }
13 function setTimeGradient() {
14   const gradientName = time.gradientName();
15   const $body = $<HTMLElement>('body');
16   $body.classList.value = gradientName; 
17
18   $body.classList.add(time.getTimePeriod());
19
20   let icon: string;
21   if(time.get24Hour() >= 5 && time.get24Hour() < 9) {
22     icon = 'morning';
23   }
24   else if(time.get24Hour() >= 9 && time.get24Hour() < 17) {
25     icon = 'afternoon';
26   }
27   else if(time.get24Hour() >= 17 && time.get24Hour() < 20) {
28     icon = 'evening'
29   }
30   else {
31     icon = 'night';
32   }
33   
34   $<HTMLElement>('#time-of-day').innerHTML = `<img src="/assets/img/icons/time-of-day/${icon}.png"> ${time.getHour()}${time.getAmPm()}`;
35 }
36
37
38 const time = new TimeManager();
39 const socket = io({
40   extraHeaders: {
41     'x-authtoken': authToken()
42   }
43 });
44 setTimeGradient();
45 setInterval(setTimeGradient, 60 * 1000);
46
47 socket.on('connect', () => {
48   console.log(`Connected: ${socket.id}`);
49 });
50
51 socket.on('authToken', (authToken: string) => {
52   console.log(`recv auth token ${authToken}`);
53   localStorage.setItem('authToken', authToken);
54 });
55
56
57 socket.on('ready', bootstrap);
58
59
60 $$<HTMLElement>('nav a').forEach(el => {
61   el.addEventListener('click', e => {
62     const el = e.target as HTMLElement;
63
64     $$<HTMLElement>('a', el.closest('nav')).forEach(el => {
65       el.classList.remove('active');
66     })
67
68     el.classList.add('active');
69
70     const targetEl = $<HTMLElement>(el.getAttribute('hx-target')!.toString());
71
72     Array.from(targetEl.parentElement!.children).forEach(el => {
73       el.classList.remove('active');
74     });
75     targetEl.classList.add('active');
76   });
77 });
78
79 $<HTMLElement>('body').addEventListener('click', e => {
80   const target = e.target as HTMLElement;
81
82   if(target!.parentElement!.classList.contains('filter')) {
83     // ok this is afunky filter object!
84     const children = Array.from(target!.parentElement!.children);
85     children.forEach(el => el.classList.remove('active'));
86     target.classList.add('active');
87
88     const dataFilter = target.getAttribute('data-filter');
89
90     const targetPane = $<HTMLElement>(`.filter-result[data-filter="${dataFilter}"]`, target.closest('.filter-container'));
91
92     if(targetPane) {
93       Array.from(targetPane!.parentElement!.children).forEach(el => {
94         if(el.getAttribute('data-filter') === dataFilter) {
95           el.classList.remove('hidden');
96           el.classList.add('active');
97         }
98         else {
99           el.classList.add('hidden');
100           el.classList.remove('active');
101         }
102       })
103     }
104   }
105
106   if(target.getAttribute('formmethod') === 'dialog' && target.getAttribute('value') === 'cancel') {
107     target.closest('dialog')?.close();
108   }
109 });
110
111 const modalMutations = new MutationObserver((list, observer) => {
112   const states = {
113     modal: false,
114     alert: false
115   };
116
117   list.forEach(mutation => {
118     switch(((mutation.target) as HTMLElement).id) {
119       case 'modal-wrapper':
120         states.modal = true;
121         break;
122       case 'alerts':
123         states.alert = true;
124         break;
125
126     }
127   });
128
129   if(states.modal) {
130     if($<HTMLElement>('#modal-wrapper').children.length) {
131       $$<HTMLDialogElement>('#modal-wrapper dialog').forEach(el => {
132         if(!el.open) {
133           el.showModal();
134         }
135       })
136     }
137   }
138
139   if(states.alert) {
140     if($<HTMLElement>('#alerts').children.length) {
141       $$<HTMLElement>('#alerts .alert').forEach(el => {
142         if(!el.getAttribute('data-dismiss-at')) {
143           const dismiss = Date.now() + 3000;
144           el.setAttribute('data-dismiss-at', dismiss.toString());
145           setTimeout(() => { el.remove(); }, 3000);
146         }
147       });
148     }
149   }
150
151 });
152
153 modalMutations.observe($<HTMLElement>('#modal-wrapper'), { childList: true });
154
155 modalMutations.observe($<HTMLElement>('#alerts'), { childList: true });
156
157
158 function bootstrap() {
159   console.log('Server connection verified');
160   configureChat(socket);
161   $$<HTMLElement>('nav a')[0].click();
162 }
163 document.body.addEventListener('htmx:configRequest', function(evt) {
164   //@ts-ignore
165   evt.detail.headers['x-authtoken'] = authToken();
166 });
167 document.body.addEventListener('htmx:load', function(evt) {
168   $$<HTMLElement>('.disabled[data-block]').forEach(el => {
169     setTimeout(() => { el.removeAttribute('disabled'); }, 3000);
170   });
171 });
172 document.body.addEventListener('htmx:beforeSwap', function(e) {
173   const el = e.target as HTMLElement;
174   if(el.id === 'chat-form') {
175     $<HTMLInputElement>('#message').value = '';
176   }
177 });
178