@ozdao/martyrs 0.2.565 → 0.2.566

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/martyrs/dist/main-B9o1iBAZ.js +943 -0
  2. package/dist/martyrs/dist/main-B9o1iBAZ.js.map +1 -0
  3. package/dist/martyrs/dist/web-BF3ijvEr.js +55 -0
  4. package/dist/martyrs/dist/web-BF3ijvEr.js.map +1 -0
  5. package/dist/martyrs/src/components/BottomSheet/BottomSheet.vue.js +96 -0
  6. package/dist/martyrs/src/components/BottomSheet/BottomSheet.vue.js.map +1 -0
  7. package/dist/martyrs/src/modules/core/views/components/layouts/App.vue.js +1 -1
  8. package/dist/martyrs/src/modules/core/views/components/layouts/Client.vue.js +1 -0
  9. package/dist/martyrs/src/modules/core/views/components/layouts/Client.vue.js.map +1 -1
  10. package/dist/martyrs/src/modules/core/views/components/sections/Walkthrough.vue.js +1 -1
  11. package/dist/martyrs/src/modules/events/components/elements/ButtonCheck.vue.js +1 -1
  12. package/dist/martyrs/src/modules/events/events.client.js +15 -12
  13. package/dist/martyrs/src/modules/events/events.client.js.map +1 -1
  14. package/dist/martyrs/src/modules/music/components/blocks/ActionButtons.vue.js +95 -0
  15. package/dist/martyrs/src/modules/music/components/blocks/ActionButtons.vue.js.map +1 -0
  16. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js +5 -2
  17. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js.map +1 -1
  18. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js +24 -24
  19. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js.map +1 -1
  20. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js +31 -6
  21. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js.map +1 -1
  22. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +120 -205
  23. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
  24. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js +9 -13
  25. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js.map +1 -1
  26. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +166 -245
  27. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
  28. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +135 -220
  29. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
  30. package/dist/martyrs/src/modules/music/components/player/FullscreenPlayer.vue.js +171 -0
  31. package/dist/martyrs/src/modules/music/components/player/FullscreenPlayer.vue.js.map +1 -0
  32. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js +31 -153
  33. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js.map +1 -1
  34. package/dist/martyrs/src/modules/music/components/player/PlayerControls.vue.js +96 -0
  35. package/dist/martyrs/src/modules/music/components/player/PlayerControls.vue.js.map +1 -0
  36. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js +55 -27
  37. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js.map +1 -1
  38. package/dist/martyrs/src/modules/music/components/player/tonar.png.js +5 -0
  39. package/dist/martyrs/src/modules/music/components/player/tonar.png.js.map +1 -0
  40. package/dist/martyrs/src/modules/music/store/albums.js +8 -2
  41. package/dist/martyrs/src/modules/music/store/albums.js.map +1 -1
  42. package/dist/martyrs/src/modules/music/store/player.js +83 -65
  43. package/dist/martyrs/src/modules/music/store/player.js.map +1 -1
  44. package/dist/martyrs/src/modules/music/store/tracks.js +4 -13
  45. package/dist/martyrs/src/modules/music/store/tracks.js.map +1 -1
  46. package/dist/martyrs/src/modules/notifications/notifications.client.js +2 -2
  47. package/dist/martyrs.css +1 -1
  48. package/dist/music.server.js +33 -6
  49. package/dist/node_modules/.pnpm/{@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1 → @capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4}/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/definitions.js +1 -0
  50. package/dist/node_modules/.pnpm/@capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/definitions.js.map +1 -0
  51. package/dist/node_modules/.pnpm/{@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1 → @capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4}/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/index.js +1 -1
  52. package/dist/node_modules/.pnpm/{@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1 → @capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4}/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/index.js.map +1 -1
  53. package/dist/node_modules/.pnpm/{@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1 → @capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4}/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/web.js +16 -1
  54. package/dist/node_modules/.pnpm/@capacitor-mlkit_barcode-scanning@7.3.0_@capacitor_core@7.4.4/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/web.js.map +1 -0
  55. package/dist/node_modules/.pnpm/{@capacitor_core@7.0.1 → @capacitor_core@7.4.4}/node_modules/@capacitor/core/dist/index.js +2 -1
  56. package/dist/node_modules/.pnpm/@capacitor_core@7.4.4/node_modules/@capacitor/core/dist/index.js.map +1 -0
  57. package/dist/node_modules/.pnpm/{@capacitor_device@7.0.0_@capacitor_core@7.0.1 → @capacitor_device@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/device/dist/esm/index.js +1 -1
  58. package/dist/node_modules/.pnpm/{@capacitor_device@7.0.0_@capacitor_core@7.0.1 → @capacitor_device@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/device/dist/esm/index.js.map +1 -1
  59. package/dist/node_modules/.pnpm/{@capacitor_device@7.0.0_@capacitor_core@7.0.1 → @capacitor_device@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/device/dist/esm/web.js +1 -1
  60. package/dist/node_modules/.pnpm/{@capacitor_device@7.0.0_@capacitor_core@7.0.1 → @capacitor_device@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/device/dist/esm/web.js.map +1 -1
  61. package/dist/node_modules/.pnpm/{@capacitor_keyboard@7.0.0_@capacitor_core@7.0.1 → @capacitor_keyboard@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/keyboard/dist/esm/definitions.js.map +1 -1
  62. package/dist/node_modules/.pnpm/{@capacitor_keyboard@7.0.0_@capacitor_core@7.0.1 → @capacitor_keyboard@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/keyboard/dist/esm/index.js +1 -1
  63. package/dist/node_modules/.pnpm/{@capacitor_keyboard@7.0.0_@capacitor_core@7.0.1 → @capacitor_keyboard@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/keyboard/dist/esm/index.js.map +1 -1
  64. package/dist/node_modules/.pnpm/{@capacitor_push-notifications@7.0.0_@capacitor_core@7.0.1 → @capacitor_push-notifications@7.0.3_@capacitor_core@7.4.4}/node_modules/@capacitor/push-notifications/dist/esm/index.js +1 -1
  65. package/dist/node_modules/.pnpm/{@capacitor_push-notifications@7.0.0_@capacitor_core@7.0.1 → @capacitor_push-notifications@7.0.3_@capacitor_core@7.4.4}/node_modules/@capacitor/push-notifications/dist/esm/index.js.map +1 -1
  66. package/dist/node_modules/.pnpm/{capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.0.1 → capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.4.4}/node_modules/capacitor-plugin-app-tracking-transparency/dist/esm/index.js +1 -1
  67. package/dist/node_modules/.pnpm/{capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.0.1 → capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.4.4}/node_modules/capacitor-plugin-app-tracking-transparency/dist/esm/index.js.map +1 -1
  68. package/dist/node_modules/.pnpm/{capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.0.1 → capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.4.4}/node_modules/capacitor-plugin-app-tracking-transparency/dist/esm/web.js +1 -1
  69. package/dist/node_modules/.pnpm/{capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.0.1 → capacitor-plugin-app-tracking-transparency@2.0.5_@capacitor_core@7.4.4}/node_modules/capacitor-plugin-app-tracking-transparency/dist/esm/web.js.map +1 -1
  70. package/dist/style.css +214 -138
  71. package/package.json +1 -1
  72. package/src/components/BottomSheet/BottomSheet.vue +4 -4
  73. package/src/modules/LAYOUT.MD +767 -0
  74. package/src/modules/core/views/components/layouts/Client.vue +1 -1
  75. package/src/modules/events/events.client.js +3 -0
  76. package/src/modules/music/components/blocks/ActionButtons.vue +74 -0
  77. package/src/modules/music/components/cards/AlbumCard.vue +1 -1
  78. package/src/modules/music/components/cards/ArtistCardSmall.vue +8 -6
  79. package/src/modules/music/components/layouts/MusicBottomPlayer.vue +94 -4
  80. package/src/modules/music/components/pages/Album.vue +55 -67
  81. package/src/modules/music/components/pages/MusicHome.vue +4 -6
  82. package/src/modules/music/components/pages/Playlist.vue +61 -70
  83. package/src/modules/music/components/pages/Track.vue +54 -71
  84. package/src/modules/music/components/player/FullscreenPlayer.vue +248 -0
  85. package/src/modules/music/components/player/MusicPlayer.vue +21 -216
  86. package/src/modules/music/components/player/PlayerControls.vue +112 -0
  87. package/src/modules/music/components/player/Visualizer.vue +151 -0
  88. package/src/modules/music/components/player/VolumeControl.vue +75 -23
  89. package/src/modules/music/components/player/tonar.png +0 -0
  90. package/src/modules/music/routes/albums.routes.js +13 -12
  91. package/src/modules/music/routes/tracks.routes.js +39 -0
  92. package/src/modules/music/store/albums.js +10 -2
  93. package/src/modules/music/store/player.js +101 -89
  94. package/src/modules/music/store/tracks.js +5 -21
  95. package/src/styles/config.scss +6 -6
  96. package/dist/node_modules/.pnpm/@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/definitions.js.map +0 -1
  97. package/dist/node_modules/.pnpm/@capacitor-mlkit_barcode-scanning@7.1.0_@capacitor_core@7.0.1/node_modules/@capacitor-mlkit/barcode-scanning/dist/esm/web.js.map +0 -1
  98. package/dist/node_modules/.pnpm/@capacitor_core@7.0.1/node_modules/@capacitor/core/dist/index.js.map +0 -1
  99. /package/dist/node_modules/.pnpm/{@capacitor_keyboard@7.0.0_@capacitor_core@7.0.1 → @capacitor_keyboard@7.0.1_@capacitor_core@7.4.4}/node_modules/@capacitor/keyboard/dist/esm/definitions.js +0 -0
@@ -3,10 +3,10 @@
3
3
  <div class="player-container">
4
4
  <div class="player">
5
5
  <!-- Track Info Section -->
6
- <div class="track-info">
6
+ <div class="track-info" @click="openFullPlayer">
7
7
  <div class="track-image">
8
- <Media
9
- :url="currentTrack?.coverUrl || (currentTrack?.album && currentTrack.album.coverUrl) || '/assets/placeholder-track.jpg'"
8
+ <Media
9
+ :url="currentTrack?.coverUrl || (currentTrack?.album && currentTrack.album.coverUrl) || '/assets/placeholder-track.jpg'"
10
10
  class="track-image-media"
11
11
  />
12
12
  </div>
@@ -14,11 +14,12 @@
14
14
  <h3 class="track-title">{{ currentTrack?.title || 'No track playing' }}</h3>
15
15
  <p class="track-artist">{{ getArtistName(currentTrack) }}</p>
16
16
  </div>
17
- <Button
18
- @click="toggleFavorite"
17
+ <Button
18
+ v-if="authState.user"
19
+ @click.stop="toggleFavorite"
19
20
  class="like-btn"
20
21
  :class="{ liked: isFavorite }"
21
- :showLoader="false"
22
+ :showLoader="false"
22
23
  :showSucces="false"
23
24
  >
24
25
  <IconLike class="like-icon"/>
@@ -28,53 +29,7 @@
28
29
  <!-- Control Section -->
29
30
  <div class="controls">
30
31
  <div class="control-buttons">
31
- <Button
32
- @click="toggleShuffle"
33
- class="control-btn secondary"
34
- :class="{ active: shuffle }"
35
- :showLoader="false"
36
- :showSucces="false"
37
- >
38
- <IconShuffle fill="rgb(var(--black))" class="control-icon"/>
39
- </Button>
40
-
41
- <Button
42
- @click="playPrevious"
43
- class="control-btn secondary"
44
- :showLoader="false"
45
- :showSucces="false"
46
- >
47
- <IconPrevious fill="rgb(var(--black))" class="control-icon"/>
48
- </Button>
49
-
50
- <Button
51
- @click="togglePlay"
52
- class="control-btn primary"
53
- :showLoader="false"
54
- :showSucces="false"
55
- >
56
- <IconPause v-if="isPlaying" fill="white" class="play-icon"/>
57
- <IconPlay fill="white" v-else class="play-icon"/>
58
- </Button>
59
-
60
- <Button
61
- @click="playNext"
62
- class="control-btn secondary"
63
- :showLoader="false"
64
- :showSucces="false"
65
- >
66
- <IconNext fill="rgb(var(--black))" class="control-icon"/>
67
- </Button>
68
-
69
- <Button
70
- @click="toggleRepeat"
71
- class="control-btn secondary"
72
- :class="{ active: repeat !== 'off' }"
73
- :showLoader="false"
74
- :showSucces="false"
75
- >
76
- <IconRepeat fill="rgb(var(--black))" class="control-icon"/>
77
- </Button>
32
+ <PlayerControls />
78
33
  </div>
79
34
 
80
35
  <!-- Progress Bar -->
@@ -84,22 +39,7 @@
84
39
  </div>
85
40
 
86
41
  <!-- Volume Section -->
87
- <div class="volume-section">
88
- <Button
89
- @click="toggleMute"
90
- class="volume-btn"
91
- :showLoader="false"
92
- :showSucces="false"
93
- >
94
- <IconVolumeMute fill="rgb(var(--black))" v-if="muted || volume < 0.005" class="volume-icon"/>
95
- <!-- <IconVolumeHalf fill="rgb(var(--black))" v-else-if="!muted && volume > 0" class="volume-icon"/> -->
96
- <IconUnMute fill="rgb(var(--black))" v-else class="volume-icon"/>
97
- </Button>
98
-
99
- <div class="volume-slider">
100
- <VolumeControl />
101
- </div>
102
- </div>
42
+ <VolumeControl />
103
43
  </div>
104
44
  </div>
105
45
  </template>
@@ -110,64 +50,31 @@ import Media from '@martyrs/src/components/Media/Media.vue';
110
50
  import Button from '@martyrs/src/components/Button/Button.vue';
111
51
  import TrackProgress from './TrackProgress.vue';
112
52
  import VolumeControl from './VolumeControl.vue';
53
+ import PlayerControls from './PlayerControls.vue';
113
54
 
114
55
  // Import icons
115
- import IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';
116
- import IconPause from '@martyrs/src/modules/icons/navigation/IconPause.vue';
117
- import IconNext from '@martyrs/src/modules/icons/navigation/IconChevronRight.vue';
118
- import IconPrevious from '@martyrs/src/modules/icons/navigation/IconChevronLeft.vue';
119
- import IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';
120
- import IconRepeat from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';
121
56
  import IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';
122
- import IconVolume from '@martyrs/src/modules/icons/navigation/IconVolume.vue';
123
- import IconUnMute from '@martyrs/src/modules/icons/navigation/IconUnMute.vue';
124
- import IconVolumeHalf from '@martyrs/src/modules/icons/navigation/IconVolume.vue';
125
- import IconVolumeMute from '@martyrs/src/modules/icons/navigation/IconMute.vue';
126
57
 
127
58
  // Import player store
128
59
  import { state as playerState, actions as playerActions } from '../../store/player.js';
60
+ import { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';
129
61
 
130
62
  // State
131
63
  const isFavorite = ref(false);
132
64
 
133
65
  // Computed properties
134
66
  const currentTrack = computed(() => playerState.currentTrack);
135
- const isPlaying = computed(() => playerState.isPlaying);
136
- const volume = computed(() => playerState.volume);
137
- const muted = computed(() => playerState.muted);
138
- const shuffle = computed(() => playerState.shuffle);
139
- const repeat = computed(() => playerState.repeat);
140
67
 
141
68
  // Methods
142
- const togglePlay = () => {
143
- playerActions.togglePlay();
144
- };
145
-
146
- const playNext = () => {
147
- playerActions.playNext();
148
- };
149
-
150
- const playPrevious = () => {
151
- playerActions.playPrevious();
152
- };
153
-
154
- const toggleShuffle = () => {
155
- playerActions.toggleShuffle();
156
- };
157
-
158
- const toggleRepeat = () => {
159
- playerActions.toggleRepeat();
160
- };
161
-
162
- const toggleMute = () => {
163
- playerActions.toggleMute();
164
- };
165
-
166
69
  const toggleFavorite = () => {
167
70
  isFavorite.value = !isFavorite.value;
168
71
  // Implement favorite track logic here
169
72
  };
170
73
 
74
+ const openFullPlayer = () => {
75
+ playerActions.toggleFullPlayer();
76
+ };
77
+
171
78
  const getArtistName = (track) => {
172
79
  if (!track) return 'Unknown Artist';
173
80
 
@@ -208,6 +115,12 @@ const getArtistName = (track) => {
208
115
  align-items: center;
209
116
  gap: 12px;
210
117
  min-width: 0;
118
+ cursor: pointer;
119
+ transition: opacity 0.2s ease;
120
+ }
121
+
122
+ .track-info:hover {
123
+ opacity: 0.8;
211
124
  }
212
125
 
213
126
  .track-image {
@@ -305,111 +218,12 @@ const getArtistName = (track) => {
305
218
  gap: 16px;
306
219
  }
307
220
 
308
- .control-btn {
309
- background: none;
310
- border: none;
311
- color: rgb(var(--grey));
312
- cursor: pointer;
313
- padding: 8px;
314
- border-radius: 50%;
315
- transition: all 0.2s ease;
316
- display: flex;
317
- align-items: center;
318
- justify-content: center;
319
- }
320
-
321
- .control-icon {
322
- width: 16px;
323
- height: 16px;
324
- }
325
-
326
- .control-btn.primary {
327
- background: rgb(var(--second));
328
- color: rgb(var(--white));
329
- width: 32px;
330
- height: 32px;
331
- }
332
-
333
- .play-icon {
334
- width: 14px;
335
- height: 14px;
336
- fill: rgb(var(--white));
337
- }
338
-
339
- .control-btn.primary:hover {
340
- background: rgb(var(--second));
341
- transform: scale(1.06);
342
- }
343
-
344
- .control-btn.secondary:hover {
345
- color: rgb(var(--white));
346
- background: rgba(var(--black),0.1);
347
- }
348
-
349
- .control-btn.secondary:hover .control-icon {
350
- fill: rgb(var(--white));
351
- }
352
-
353
- .control-btn.active {
354
- color: rgb(var(--main));
355
- background: rgba(var(--second),0.1);
356
- }
357
-
358
- .control-btn.active .control-icon {
359
- fill: rgb(var(--main));
360
- }
361
-
362
- .control-btn.active:hover {
363
- color: rgb(var(--main));
364
- opacity: 0.8;
365
- background: rgba(var(--second),0.2);
366
- }
367
-
368
221
  /* Progress Section */
369
222
  .progress-section {
370
223
  width: 100%;
371
224
  max-width: 600px;
372
225
  }
373
226
 
374
- /* Volume Section */
375
- .volume-section {
376
- display: flex;
377
- align-items: center;
378
- gap: 12px;
379
- justify-content: flex-end;
380
- }
381
-
382
- .volume-btn {
383
- background: none;
384
- border: none;
385
- color: rgb(var(--grey));
386
- cursor: pointer;
387
- padding: 8px;
388
- border-radius: 50%;
389
- transition: all 0.2s ease;
390
- display: flex;
391
- align-items: center;
392
- justify-content: center;
393
- }
394
-
395
- .volume-icon {
396
- width: 16px;
397
- height: 16px;
398
- }
399
-
400
- .volume-btn:hover {
401
- color: rgb(var(--white));
402
- background: rgba(var(--white),0.1);
403
- }
404
-
405
- .volume-btn:hover .volume-icon {
406
- fill: rgb(var(--white));
407
- }
408
-
409
- .volume-slider {
410
- width: 100px;
411
- }
412
-
413
227
  /* Responsive Design */
414
228
  @media (max-width: 768px) {
415
229
  .player {
@@ -428,15 +242,6 @@ const getArtistName = (track) => {
428
242
  order: 2;
429
243
  }
430
244
 
431
- .volume-section {
432
- order: 3;
433
- justify-content: center;
434
- }
435
-
436
- .volume-slider {
437
- width: 120px;
438
- }
439
-
440
245
  .progress-section {
441
246
  max-width: 100%;
442
247
  }
@@ -0,0 +1,112 @@
1
+ <!-- components/player/PlayerControls.vue -->
2
+ <template>
3
+ <div class="flex-nowrap flex gap-thin flex-v-center">
4
+ <!-- Shuffle -->
5
+ <Button
6
+ @click="playerActions.toggleShuffle()"
7
+ class="control-btn secondary"
8
+ :class="{ active: playerState.shuffle }"
9
+ :showLoader="false"
10
+ :showSucces="false"
11
+ >
12
+ <IconShuffle fill="rgb(var(--black))" class="control-icon"/>
13
+ </Button>
14
+
15
+ <!-- Previous -->
16
+ <Button
17
+ @click="playerActions.playPrevious()"
18
+ class="control-btn secondary"
19
+ :showLoader="false"
20
+ :showSucces="false"
21
+ >
22
+ <IconPrevious fill="rgb(var(--black))" class="control-icon"/>
23
+ </Button>
24
+
25
+ <!-- Play/Pause -->
26
+ <Button
27
+ @click="playerActions.togglePlay()"
28
+ class="control-btn primary"
29
+ :showLoader="false"
30
+ :showSucces="false"
31
+ >
32
+ <div
33
+ :class="{'paused': !playerState.isPlaying}"
34
+ class="play-icon pauseplay-btn"
35
+ >
36
+ <div class="radius-small d1"></div>
37
+ <div class="radius-small d2"></div>
38
+ </div>
39
+ </Button>
40
+
41
+ <!-- Next -->
42
+ <Button
43
+ @click="playerActions.playNext()"
44
+ class="control-btn secondary"
45
+ :showLoader="false"
46
+ :showSucces="false"
47
+ >
48
+ <IconNext fill="rgb(var(--black))" class="control-icon"/>
49
+ </Button>
50
+
51
+ <!-- Repeat -->
52
+ <Button
53
+ @click="playerActions.toggleRepeat()"
54
+ class="control-btn secondary"
55
+ :class="{ active: playerState.repeat }"
56
+ :showLoader="false"
57
+ :showSucces="false"
58
+ >
59
+ <IconRepeat fill="rgb(var(--black))" class="control-icon"/>
60
+ </Button>
61
+ </div>
62
+ </template>
63
+
64
+ <script setup>
65
+ import Button from '@martyrs/src/components/Button/Button.vue';
66
+ import IconPrevious from '@martyrs/src/modules/icons/navigation/IconChevronLeft.vue';
67
+ import IconNext from '@martyrs/src/modules/icons/navigation/IconChevronRight.vue';
68
+ import IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';
69
+ import IconRepeat from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';
70
+ import { state as playerState, actions as playerActions } from '../../store/player.js';
71
+ </script>
72
+
73
+ <style scoped>
74
+ .pauseplay-btn {
75
+ --bars-anim-duration: 300ms;
76
+ --clip-anim-duration: 150ms;
77
+ position: relative;
78
+ background-color: transparent;
79
+ border: none;
80
+ outline: none;
81
+ -webkit-tap-highlight-color: transparent;
82
+ transition: clip-path ease-out var(--clip-anim-duration);
83
+ clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 0);
84
+ }
85
+
86
+ .pauseplay-btn.paused {
87
+ clip-path: polygon(0 0, 0 100%, 100% 50%, 100% 50%);
88
+ }
89
+
90
+ .pauseplay-btn div {
91
+ position: absolute;
92
+ top: 0;
93
+ width: 30%;
94
+ height: 100%;
95
+ background: white;
96
+ transition: transform ease-out var(--bars-anim-duration);
97
+ }
98
+
99
+ .pauseplay-btn.paused div {
100
+ transform: scaleX(2);
101
+ }
102
+
103
+ .pauseplay-btn div.d1 {
104
+ left: 0;
105
+ transform-origin: left;
106
+ }
107
+
108
+ .pauseplay-btn div.d2 {
109
+ right: 0;
110
+ transform-origin: right;
111
+ }
112
+ </style>
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <div class="">
3
+ <canvas class="w-100 h-100" style="transform: rotate(-90deg)" id="audiowaves" ref="audiowaves"/>
4
+ </div>
5
+ </template>
6
+
7
+
8
+ <style lang="scss">
9
+ </style>
10
+
11
+ <script setup>
12
+ import { ref, reactive,onMounted, onUnmounted, watchEffect } from 'vue';
13
+ import { App } from '@capacitor/app';
14
+
15
+ import * as radioStore from './radio.store.js';
16
+
17
+ const props = defineProps({
18
+ options: {
19
+ type: Object,
20
+ default: {
21
+ position: false
22
+ }
23
+ }
24
+ })
25
+
26
+ const audiowaves = ref(null)
27
+ const cleanupResources = ref([]);
28
+
29
+ let audio, context, source, splitter, procNode, analChannel_0, analChannel_1;
30
+
31
+ App.addListener('appStateChange', ({ isActive }) => {
32
+ isActive ? initFullPlayer() : removeFullPlayer()
33
+ });
34
+
35
+ const stopAudioAndContext = async () => {
36
+ if (source) await source.disconnect();
37
+ if (context) await context.close();
38
+ };
39
+
40
+ async function initFullPlayer () {
41
+ audio = await radioStore.getPlayerInstance()
42
+ context = await radioStore.getContextInstance()
43
+ source = await radioStore.getSourceInstance(context,audio)
44
+
45
+ let c = document.getElementById('audiowaves');
46
+
47
+ if (c) {
48
+ let $ = c.getContext('2d');
49
+ let w = c.width = global.innerWidth;
50
+ let h = c.height = global.innerHeight;
51
+
52
+ //number of frequency bins to be displayed for each channel
53
+ let NUMBER_OF_COLUMNS = 1300;
54
+ let COLUMN_WIDTH = w / NUMBER_OF_COLUMNS;
55
+ let MIDDLE_HEIGHT = Math.round(h / 2);
56
+ let ANALYSERS_FFT_SIZE = 4096;
57
+ let HUE_STEP = Math.round(360 / 256);
58
+
59
+ let i, value;
60
+
61
+ //color step for frequency values
62
+ visualise(new Array(NUMBER_OF_COLUMNS), new Array(NUMBER_OF_COLUMNS));
63
+
64
+ analChannel_0 = context.createAnalyser();
65
+ analChannel_1 = context.createAnalyser();
66
+
67
+ analChannel_0.fftSize = analChannel_1.fftSize = ANALYSERS_FFT_SIZE;
68
+
69
+ splitter = context.createChannelSplitter(2);
70
+ splitter.connect(analChannel_0, 0);
71
+ splitter.connect(analChannel_1, 1);
72
+
73
+ //processing node callback to be called every 1024 samples collected
74
+ procNode = context.createScriptProcessor(1024);
75
+ procNode.connect(context.destination);
76
+ procNode.onaudioprocess = function(event) {
77
+ let array0 = new Uint8Array(analChannel_0.frequencyBinCount);
78
+ analChannel_0.getByteFrequencyData(array0);
79
+ let array1 = new Uint8Array(analChannel_1.frequencyBinCount);
80
+ analChannel_1.getByteFrequencyData(array1);
81
+
82
+ visualise(array0, array1);
83
+ }
84
+
85
+ source.connect(procNode);
86
+ source.connect(splitter);
87
+
88
+ cleanupResources.value.push(() => procNode.disconnect());
89
+ cleanupResources.value.push(() => splitter.disconnect());
90
+ cleanupResources.value.push(() => analChannel_0.disconnect());
91
+ cleanupResources.value.push(() => analChannel_1.disconnect());
92
+ cleanupResources.value.push(stopAudioAndContext);
93
+
94
+
95
+ function visualise(array0, array1) {
96
+
97
+ $.clearRect(0, 0, w, h);
98
+ $.fillStyle = 'black';
99
+ $.fillRect(0, 0, w, h);
100
+
101
+
102
+ for (i = 0; i < NUMBER_OF_COLUMNS; i++) {
103
+ value = array0[i] * radioStore.state.volume || 1;
104
+ $.fillStyle = 'hsl(' + ((value * HUE_STEP) - 180) + ', 100%, 81%)'; // Adjusted saturation and lightness
105
+ $.fillRect(i * COLUMN_WIDTH, MIDDLE_HEIGHT - (value * 0.7 || 1), COLUMN_WIDTH, 256);
106
+ }
107
+
108
+ $.clearRect(0, h / 2, w, h / 2);
109
+ $.fillStyle = 'black';
110
+ $.fillRect(0, h / 2, w, h / 2);
111
+
112
+ for (i = 0; i < NUMBER_OF_COLUMNS; i++) {
113
+ value = array1[i] * radioStore.state.volume || 1;
114
+ $.fillStyle = 'hsl(' + ((value * HUE_STEP) - 180) + ', 100%, 81%)'; // Adjusted saturation and lightness
115
+ $.fillRect(i * COLUMN_WIDTH, MIDDLE_HEIGHT + 1, COLUMN_WIDTH, value * 0.7 || 1);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ async function removeFullPlayer () {
122
+ cleanupResources.value.forEach(cleanup => cleanup());
123
+
124
+ audio = null
125
+ context = null
126
+ source = null
127
+
128
+ radioStore.state.context = null
129
+ radioStore.state.source = null
130
+ radioStore.state.gain = null
131
+ radioStore.state.player = null
132
+
133
+
134
+ let playerNew = radioStore.getPlayerInstance()
135
+ radioStore.state.player.volume = radioStore.state.volume;
136
+
137
+ if (radioStore.state.isPlaying) {
138
+ await playerNew.play()
139
+ }
140
+ }
141
+
142
+ onMounted(async() => {
143
+ await initFullPlayer()
144
+ });
145
+
146
+ onUnmounted(async() => {
147
+ await removeFullPlayer()
148
+ App.removeAllListeners()
149
+ });
150
+
151
+ </script>
@@ -1,28 +1,42 @@
1
1
  <!-- components/player/VolumeControl.vue -->
2
2
  <template>
3
- <div class="volume-control">
4
- <div
5
- ref="volumeBarContainer"
6
- class="volume-bar-container"
7
- @click="setVolume"
8
- @mousedown="startVolumeChange"
9
- @mousemove="updateVolumePosition"
10
- @mouseup="endVolumeChange"
11
- @mouseleave="endVolumeChange"
3
+ <div class="volume-section">
4
+ <Button
5
+ @click="playerActions.toggleMute()"
6
+ class="volume-btn"
7
+ :showLoader="false"
8
+ :showSucces="false"
12
9
  >
13
- <div
14
- class="volume-track"
15
- :style="{
16
- background: `linear-gradient(to right, rgb(var(--black)) 0%, rgb(var(--black)) ${volumePercentage}%, rgb(var(--grey)) ${volumePercentage}%, rgb(var(--grey)) 100%)`
17
- }"
18
- >
19
- <div
20
- class="volume-thumb"
21
- :style="{
22
- left: `${volumePercentage}%`,
23
- opacity: muted ? 0 : 1
24
- }"
25
- ></div>
10
+ <IconMute v-if="muted || volume < 0.005" fill="rgb(var(--black))" class="volume-icon"/>
11
+ <IconUnMute v-else fill="rgb(var(--black))" class="volume-icon"/>
12
+ </Button>
13
+
14
+ <div class="volume-slider">
15
+ <div class="volume-control">
16
+ <div
17
+ ref="volumeBarContainer"
18
+ class="volume-bar-container"
19
+ @click="setVolume"
20
+ @mousedown="startVolumeChange"
21
+ @mousemove="updateVolumePosition"
22
+ @mouseup="endVolumeChange"
23
+ @mouseleave="endVolumeChange"
24
+ >
25
+ <div
26
+ class="volume-track"
27
+ :style="{
28
+ background: `linear-gradient(to right, rgb(var(--black)) 0%, rgb(var(--black)) ${volumePercentage}%, rgb(var(--grey)) ${volumePercentage}%, rgb(var(--grey)) 100%)`
29
+ }"
30
+ >
31
+ <div
32
+ class="volume-thumb"
33
+ :style="{
34
+ left: `${volumePercentage}%`,
35
+ opacity: muted ? 0 : 1
36
+ }"
37
+ ></div>
38
+ </div>
39
+ </div>
26
40
  </div>
27
41
  </div>
28
42
  </div>
@@ -30,6 +44,9 @@
30
44
 
31
45
  <script setup>
32
46
  import { ref, computed } from 'vue';
47
+ import Button from '@martyrs/src/components/Button/Button.vue';
48
+ import IconMute from '@martyrs/src/modules/icons/navigation/IconMute.vue';
49
+ import IconUnMute from '@martyrs/src/modules/icons/navigation/IconUnMute.vue';
33
50
 
34
51
  // Import player store
35
52
  import { state as playerState, actions as playerActions } from '../../store/player.js';
@@ -83,6 +100,41 @@ const endVolumeChange = () => {
83
100
  </script>
84
101
 
85
102
  <style scoped>
103
+ .volume-section {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 12px;
107
+ justify-content: center;
108
+ width: 100%;
109
+ }
110
+
111
+ .volume-btn {
112
+ background: none;
113
+ border: none;
114
+ color: rgb(var(--black));
115
+ cursor: pointer;
116
+ padding: 8px;
117
+ border-radius: 50%;
118
+ transition: all 0.2s ease;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ flex-shrink: 0;
123
+ }
124
+
125
+ .volume-icon {
126
+ width: 16px;
127
+ height: 16px;
128
+ }
129
+
130
+ .volume-btn:hover {
131
+ background: rgba(var(--black),0.1);
132
+ }
133
+
134
+ .volume-slider {
135
+ flex: 1;
136
+ }
137
+
86
138
  .volume-control {
87
139
  width: 100%;
88
140
  }
@@ -109,7 +161,7 @@ const endVolumeChange = () => {
109
161
  top: 50%;
110
162
  width: 12px;
111
163
  height: 12px;
112
- background: rgb(var(--black));
164
+ background: rgb(var(--black));
113
165
  border: 1px solid rgba(var(--white),0.1);
114
166
  border-radius: 50%;
115
167
  transform: translate(-50%, -50%);