@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.
- package/dist/martyrs/dist/main-B9o1iBAZ.js +943 -0
- package/dist/martyrs/dist/main-B9o1iBAZ.js.map +1 -0
- package/dist/martyrs/dist/web-BF3ijvEr.js +55 -0
- package/dist/martyrs/dist/web-BF3ijvEr.js.map +1 -0
- package/dist/martyrs/src/components/BottomSheet/BottomSheet.vue.js +96 -0
- package/dist/martyrs/src/components/BottomSheet/BottomSheet.vue.js.map +1 -0
- package/dist/martyrs/src/modules/core/views/components/layouts/App.vue.js +1 -1
- package/dist/martyrs/src/modules/core/views/components/layouts/Client.vue.js +1 -0
- package/dist/martyrs/src/modules/core/views/components/layouts/Client.vue.js.map +1 -1
- package/dist/martyrs/src/modules/core/views/components/sections/Walkthrough.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/elements/ButtonCheck.vue.js +1 -1
- package/dist/martyrs/src/modules/events/events.client.js +15 -12
- package/dist/martyrs/src/modules/events/events.client.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/blocks/ActionButtons.vue.js +95 -0
- package/dist/martyrs/src/modules/music/components/blocks/ActionButtons.vue.js.map +1 -0
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js +5 -2
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js +24 -24
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js +31 -6
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +120 -205
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js +9 -13
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +166 -245
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +135 -220
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/FullscreenPlayer.vue.js +171 -0
- package/dist/martyrs/src/modules/music/components/player/FullscreenPlayer.vue.js.map +1 -0
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js +31 -153
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/PlayerControls.vue.js +96 -0
- package/dist/martyrs/src/modules/music/components/player/PlayerControls.vue.js.map +1 -0
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js +55 -27
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/tonar.png.js +5 -0
- package/dist/martyrs/src/modules/music/components/player/tonar.png.js.map +1 -0
- package/dist/martyrs/src/modules/music/store/albums.js +8 -2
- package/dist/martyrs/src/modules/music/store/albums.js.map +1 -1
- package/dist/martyrs/src/modules/music/store/player.js +83 -65
- package/dist/martyrs/src/modules/music/store/player.js.map +1 -1
- package/dist/martyrs/src/modules/music/store/tracks.js +4 -13
- package/dist/martyrs/src/modules/music/store/tracks.js.map +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.js +2 -2
- package/dist/martyrs.css +1 -1
- package/dist/music.server.js +33 -6
- 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
- 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
- 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
- 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
- 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
- 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
- package/dist/node_modules/.pnpm/{@capacitor_core@7.0.1 → @capacitor_core@7.4.4}/node_modules/@capacitor/core/dist/index.js +2 -1
- package/dist/node_modules/.pnpm/@capacitor_core@7.4.4/node_modules/@capacitor/core/dist/index.js.map +1 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/dist/style.css +214 -138
- package/package.json +1 -1
- package/src/components/BottomSheet/BottomSheet.vue +4 -4
- package/src/modules/LAYOUT.MD +767 -0
- package/src/modules/core/views/components/layouts/Client.vue +1 -1
- package/src/modules/events/events.client.js +3 -0
- package/src/modules/music/components/blocks/ActionButtons.vue +74 -0
- package/src/modules/music/components/cards/AlbumCard.vue +1 -1
- package/src/modules/music/components/cards/ArtistCardSmall.vue +8 -6
- package/src/modules/music/components/layouts/MusicBottomPlayer.vue +94 -4
- package/src/modules/music/components/pages/Album.vue +55 -67
- package/src/modules/music/components/pages/MusicHome.vue +4 -6
- package/src/modules/music/components/pages/Playlist.vue +61 -70
- package/src/modules/music/components/pages/Track.vue +54 -71
- package/src/modules/music/components/player/FullscreenPlayer.vue +248 -0
- package/src/modules/music/components/player/MusicPlayer.vue +21 -216
- package/src/modules/music/components/player/PlayerControls.vue +112 -0
- package/src/modules/music/components/player/Visualizer.vue +151 -0
- package/src/modules/music/components/player/VolumeControl.vue +75 -23
- package/src/modules/music/components/player/tonar.png +0 -0
- package/src/modules/music/routes/albums.routes.js +13 -12
- package/src/modules/music/routes/tracks.routes.js +39 -0
- package/src/modules/music/store/albums.js +10 -2
- package/src/modules/music/store/player.js +101 -89
- package/src/modules/music/store/tracks.js +5 -21
- package/src/styles/config.scss +6 -6
- 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
- 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
- package/dist/node_modules/.pnpm/@capacitor_core@7.0.1/node_modules/@capacitor/core/dist/index.js.map +0 -1
- /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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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-
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
class="volume-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
>
|
|
19
|
-
<div
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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%);
|
|
Binary file
|