@meeovi/layer-lists 1.0.2 → 1.0.4

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 (47) hide show
  1. package/README.md +111 -0
  2. package/package.json +15 -3
  3. package/app/components/features/archived.vue +0 -64
  4. package/app/components/features/bookmarks.vue +0 -64
  5. package/app/components/features/lists.vue +0 -61
  6. package/app/components/features/starred.vue +0 -64
  7. package/app/components/lists/ListItemCard.vue +0 -190
  8. package/app/components/lists/add-bookmark.vue +0 -52
  9. package/app/components/lists/add-list-item.vue +0 -88
  10. package/app/components/lists/add-list.vue +0 -57
  11. package/app/components/lists/lists.vue +0 -6
  12. package/app/components/lists/listsettings.vue +0 -145
  13. package/app/components/lists/update-bookmark.vue +0 -267
  14. package/app/components/lists/update-list.vue +0 -192
  15. package/app/components/media/MediaPlayer.vue +0 -302
  16. package/app/components/partials/addtolist.vue +0 -233
  17. package/app/components/partials/createListBtn.vue +0 -95
  18. package/app/components/partials/listBtn.vue +0 -35
  19. package/app/components/related/list.vue +0 -33
  20. package/app/components/related/relatedlists.vue +0 -43
  21. package/app/components/tasks/TaskItem.vue +0 -204
  22. package/app/composables/bookmarks/createBookmark.js +0 -30
  23. package/app/composables/bookmarks/deleteBookmark.js +0 -15
  24. package/app/composables/bookmarks/updateBookmark.js +0 -15
  25. package/app/composables/config.ts +0 -17
  26. package/app/composables/content/uploadFiles.js +0 -41
  27. package/app/composables/globals/useDirectusForm.ts +0 -1
  28. package/app/composables/lists/createList.js +0 -25
  29. package/app/composables/lists/deleteList.js +0 -14
  30. package/app/composables/lists/updateList.js +0 -20
  31. package/app/composables/lists/useBookmarks.js +0 -69
  32. package/app/composables/lists/useLists.js +0 -120
  33. package/app/composables/lists/usePlaylist.js +0 -64
  34. package/app/composables/lists/useSaved.js +0 -29
  35. package/app/composables/lists/useTasks.js +0 -86
  36. package/app/composables/lists/useWishlist.js +0 -51
  37. package/app/composables/providers/atproto.ts +0 -156
  38. package/app/composables/providers/directus.ts +0 -49
  39. package/app/composables/providers/memory.ts +0 -88
  40. package/app/composables/registry.ts +0 -13
  41. package/app/composables/types.ts +0 -35
  42. package/app/composables/useLists.ts +0 -20
  43. package/app/composables/utils/transforms.ts +0 -42
  44. package/app/composables/utils/validation.ts +0 -21
  45. package/app/pages/lists/bookmark/[id].vue +0 -76
  46. package/app/pages/lists/index.vue +0 -152
  47. package/app/pages/lists/list/[...slug].vue +0 -233
@@ -1,192 +0,0 @@
1
- <template>
2
- <div>
3
- <v-row justify="center">
4
- <v-card>
5
- <form @submit.prevent="handleSubmit">
6
- <v-toolbar dark color="primary">
7
- <v-btn icon dark @click="dialog = false">
8
- <v-icon icon="fas:fa fa-circle-xmark"></v-icon>
9
- </v-btn>
10
- <v-card-title>
11
- <span class="text-h6">Create a new Space</span>
12
- </v-card-title>
13
- </v-toolbar>
14
- <v-card-text>
15
- <v-container>
16
- <v-row>
17
- <v-col cols="12">
18
- <v-text-field v-model="listData.name" label="List Name" required></v-text-field>
19
- </v-col>
20
- <v-col cols="6">
21
- <v-select v-model="listData.type" label="Type"
22
- :items="['List', 'Registry', 'Playlist', 'Todo']"></v-select>
23
- </v-col>
24
- <v-col cols="6">
25
- <v-select v-model="listData.status" label="Status"
26
- :items="['Public', 'Private']"></v-select>
27
- </v-col>
28
- <v-col cols="12">
29
- <v-file-input @change="handleImageUpload" clearable
30
- density="compact" prepend-icon="fas:fa fa-image" accept="image/*"
31
- label="Image for List" variant="solo-inverted" />
32
- </v-col>
33
- <v-col cols="12">
34
- <v-textarea v-model="listData.description" label="List Description"></v-textarea>
35
- </v-col>
36
- <v-col cols="12">
37
- <v-card title="Choose a Product for your List">
38
- <v-card-text>
39
- <v-text-field density="compact" variant="solo"
40
- label="Search Meeovi for products" append-inner-icon="fas:fa fa-search"
41
- single-line hide-details></v-text-field>
42
- <div class="d-flex pa-4">
43
- <v-checkbox-btn v-model="includeFiles" class="pe-2" color="orange">
44
- </v-checkbox-btn>
45
- <!--<NuxtLink :to="`/product/${products.id}`">
46
- <v-card class="ma-4" height="580" width="250" @click="toggle">
47
- <NuxtImg loading="lazy" class="align-end text-white" height="280"
48
- :src="`${products.featuredAsset.preview}`" :alt="products.name" cover />
49
-
50
- <v-card-title class="pt-4">
51
- {{ products.name }}
52
- </v-card-title>
53
-
54
- <v-card-text>
55
- <div>Sku: {{ products.variants.sku }}</div>
56
- </v-card-text>
57
-
58
- <v-card-actions>
59
- <v-card-title>$ {{ products.variants.price }}
60
- </v-card-title>
61
- </v-card-actions>
62
- <div class="d-flex fill-height align-center justify-center">
63
- <v-scale-transition>
64
- <v-icon v-if="isSelected" color="white" size="48"
65
- icon="mdi-close-circle-outline"></v-icon>
66
- </v-scale-transition>
67
- </div>
68
- </v-card>
69
- </NuxtLink>-->
70
- </div>
71
- </v-card-text>
72
- </v-card>
73
- </v-col>
74
- </v-row>
75
- </v-container>
76
- <small>*indicates required field</small>
77
- </v-card-text>
78
- <v-divider class="mt-12"></v-divider>
79
- <v-card-actions>
80
- <v-btn color="blue-darken-1" variant="text" type="submit" @click="resetForm = false">
81
- Reset
82
- </v-btn>
83
- <v-btn color="blue-darken-1" variant="text" type="submit">
84
- Update
85
- </v-btn>
86
- </v-card-actions>
87
- </form>
88
- </v-card>
89
- </v-row>
90
- </div>
91
- </template>
92
-
93
- <script setup>
94
- import { ref, onMounted } from 'vue';
95
- import { useRoute, useRouter } from 'vue-router';
96
- import uploadFiles from '~//composables/uploadFiles';
97
- import updateList from '~/app/composables/lists/updateList';
98
-
99
- const route = useRoute();
100
- const router = useRouter();
101
-
102
- const listData = ref({
103
- id: '', // Add this to store the list ID
104
- name: '',
105
- type: '',
106
- status: '',
107
- description: '',
108
- image: null,
109
- });
110
-
111
- const dialog = ref(false);
112
- const includeFiles = ref(true);
113
- const imageFile = ref(null);
114
- const loading = ref(false);
115
-
116
- // Function to fetch existing list data
117
- const fetchListData = async () => {
118
- try {
119
- const { $directus, $readItem } = useNuxtApp();
120
- const listId = route.params.id; // Assuming you're passing the ID in the route
121
- const response = await $directus.request(readItem('lists', listId));
122
-
123
- // Populate the form with existing data
124
- listData.value = {
125
- id: response.id,
126
- name: response.name,
127
- type: response.type,
128
- status: response.status,
129
- description: response.description,
130
- image: response.image
131
- };
132
- } catch (error) {
133
- console.error('Error fetching list:', error);
134
- }
135
- };
136
-
137
- // Load existing data when component mounts
138
- onMounted(() => {
139
- if (route.params.id) {
140
- fetchListData();
141
- }
142
- });
143
-
144
- const handleImageUpload = (event) => {
145
- imageFile.value = event.target.files[0];
146
- };
147
-
148
- const resetForm = () => {
149
- listData.value = {
150
- name: '',
151
- type: '',
152
- status: '',
153
- description: '',
154
- image: null,
155
- };
156
- imageFile.value = null;
157
- };
158
-
159
- const handleSubmit = async () => {
160
- try {
161
- loading.value = true;
162
-
163
- // Handle image upload if there's a new image
164
- if (imageFile.value) {
165
- const uploadedFiles = await uploadFiles({
166
- imageFile: imageFile.value,
167
- });
168
- listData.value.image = uploadedFiles.imageId;
169
- }
170
-
171
- // Update the list
172
- const updatedList = await updateList(route.params.id, {
173
- name: listData.value.name,
174
- type: listData.value.type,
175
- status: listData.value.status,
176
- description: listData.value.description,
177
- image: listData.value.image,
178
- });
179
-
180
- console.log('List updated successfully:', updatedList);
181
-
182
- // Show success message (you can implement your preferred notification system)
183
- alert('List updated successfully');
184
-
185
- } catch (error) {
186
- console.error('Error updating list:', error);
187
- alert('Error updating list: ' + error.message);
188
- } finally {
189
- loading.value = false;
190
- }
191
- };
192
- </script>
@@ -1,302 +0,0 @@
1
- <template>
2
- <v-card class="media-player">
3
- <div v-if="currentMedia">
4
- <!-- Video Player -->
5
- <video
6
- v-if="currentMedia.media_type === 'video'"
7
- ref="videoPlayer"
8
- :src="currentMedia.url"
9
- :poster="currentMedia.thumbnail"
10
- controls
11
- @loadedmetadata="onMediaLoaded"
12
- @timeupdate="onTimeUpdate"
13
- @ended="onMediaEnded"
14
- class="w-100"
15
- />
16
-
17
- <!-- Audio Player -->
18
- <div v-else-if="currentMedia.media_type === 'audio'" class="audio-player">
19
- <div class="d-flex align-center pa-4">
20
- <v-img
21
- :src="currentMedia.thumbnail || '/default-audio.png'"
22
- width="80"
23
- height="80"
24
- class="rounded me-4"
25
- />
26
- <div class="grow">
27
- <h3 class="text-h6">{{ currentMedia.title }}</h3>
28
- <p class="text-body-2 text-medium-emphasis">
29
- {{ currentMedia.metadata?.artist || 'Unknown Artist' }}
30
- </p>
31
- </div>
32
- </div>
33
-
34
- <audio
35
- ref="audioPlayer"
36
- :src="currentMedia.url"
37
- @loadedmetadata="onMediaLoaded"
38
- @timeupdate="onTimeUpdate"
39
- @ended="onMediaEnded"
40
- style="display: none;"
41
- />
42
- </div>
43
- </div>
44
-
45
- <!-- Controls -->
46
- <v-card-actions class="px-4 py-2">
47
- <v-btn
48
- icon
49
- @click="previousTrack"
50
- :disabled="currentIndex === 0"
51
- >
52
- <v-icon>mdi-skip-previous</v-icon>
53
- </v-btn>
54
-
55
- <v-btn
56
- icon
57
- @click="togglePlayPause"
58
- color="primary"
59
- size="large"
60
- >
61
- <v-icon>{{ isPlaying ? 'mdi-pause' : 'mdi-play' }}</v-icon>
62
- </v-btn>
63
-
64
- <v-btn
65
- icon
66
- @click="nextTrack"
67
- :disabled="currentIndex === playlist.length - 1"
68
- >
69
- <v-icon>mdi-skip-next</v-icon>
70
- </v-btn>
71
-
72
- <v-spacer />
73
-
74
- <span class="text-caption">{{ formatTime(currentTime) }}</span>
75
- <v-slider
76
- v-model="currentTime"
77
- :max="duration"
78
- @update:model-value="seekTo"
79
- class="mx-4"
80
- style="min-width: 200px;"
81
- hide-details
82
- />
83
- <span class="text-caption">{{ formatTime(duration) }}</span>
84
-
85
- <v-spacer />
86
-
87
- <v-btn
88
- icon
89
- @click="toggleShuffle"
90
- :color="shuffle ? 'primary' : 'default'"
91
- >
92
- <v-icon>mdi-shuffle</v-icon>
93
- </v-btn>
94
-
95
- <v-btn
96
- icon
97
- @click="toggleRepeat"
98
- :color="repeat !== 'none' ? 'primary' : 'default'"
99
- >
100
- <v-icon>{{ repeatIcon }}</v-icon>
101
- </v-btn>
102
-
103
- <v-btn icon @click="showPlaylist = !showPlaylist">
104
- <v-icon>mdi-playlist-music</v-icon>
105
- </v-btn>
106
- </v-card-actions>
107
-
108
- <!-- Playlist -->
109
- <v-expand-transition>
110
- <div v-show="showPlaylist">
111
- <v-divider />
112
- <v-list class="playlist-list" max-height="300" style="overflow-y: auto;">
113
- <v-list-item
114
- v-for="(item, index) in playlist"
115
- :key="item.id"
116
- @click="playTrack(index)"
117
- :active="index === currentIndex"
118
- >
119
- <template v-slot:prepend>
120
- <v-img
121
- :src="item.thumbnail || '/default-audio.png'"
122
- width="40"
123
- height="40"
124
- class="rounded me-3"
125
- />
126
- </template>
127
-
128
- <v-list-item-title>{{ item.title }}</v-list-item-title>
129
- <v-list-item-subtitle>
130
- {{ item.metadata?.artist || 'Unknown Artist' }}
131
- </v-list-item-subtitle>
132
-
133
- <template v-slot:append>
134
- <span class="text-caption">{{ formatTime(item.duration) }}</span>
135
- </template>
136
- </v-list-item>
137
- </v-list>
138
- </div>
139
- </v-expand-transition>
140
- </v-card>
141
- </template>
142
-
143
- <script setup>
144
- const props = defineProps({
145
- playlist: {
146
- type: Array,
147
- required: true
148
- },
149
- autoplay: {
150
- type: Boolean,
151
- default: false
152
- }
153
- })
154
-
155
- const currentIndex = ref(0)
156
- const isPlaying = ref(false)
157
- const currentTime = ref(0)
158
- const duration = ref(0)
159
- const shuffle = ref(false)
160
- const repeat = ref('none') // 'none', 'one', 'all'
161
- const showPlaylist = ref(false)
162
-
163
- const videoPlayer = ref(null)
164
- const audioPlayer = ref(null)
165
-
166
- const currentMedia = computed(() => {
167
- return props.playlist[currentIndex.value] || null
168
- })
169
-
170
- const currentPlayer = computed(() => {
171
- return currentMedia.value?.media_type === 'video' ? videoPlayer.value : audioPlayer.value
172
- })
173
-
174
- const repeatIcon = computed(() => {
175
- switch (repeat.value) {
176
- case 'one': return 'mdi-repeat-once'
177
- case 'all': return 'mdi-repeat'
178
- default: return 'mdi-repeat-off'
179
- }
180
- })
181
-
182
- const togglePlayPause = () => {
183
- if (!currentPlayer.value) return
184
-
185
- if (isPlaying.value) {
186
- currentPlayer.value.pause()
187
- } else {
188
- currentPlayer.value.play()
189
- }
190
- isPlaying.value = !isPlaying.value
191
- }
192
-
193
- const playTrack = (index) => {
194
- currentIndex.value = index
195
- nextTick(() => {
196
- if (currentPlayer.value) {
197
- currentPlayer.value.play()
198
- isPlaying.value = true
199
- }
200
- })
201
- }
202
-
203
- const nextTrack = () => {
204
- if (shuffle.value) {
205
- currentIndex.value = Math.floor(Math.random() * props.playlist.length)
206
- } else if (currentIndex.value < props.playlist.length - 1) {
207
- currentIndex.value++
208
- } else if (repeat.value === 'all') {
209
- currentIndex.value = 0
210
- }
211
-
212
- nextTick(() => {
213
- if (currentPlayer.value && isPlaying.value) {
214
- currentPlayer.value.play()
215
- }
216
- })
217
- }
218
-
219
- const previousTrack = () => {
220
- if (currentIndex.value > 0) {
221
- currentIndex.value--
222
- } else if (repeat.value === 'all') {
223
- currentIndex.value = props.playlist.length - 1
224
- }
225
-
226
- nextTick(() => {
227
- if (currentPlayer.value && isPlaying.value) {
228
- currentPlayer.value.play()
229
- }
230
- })
231
- }
232
-
233
- const seekTo = (time) => {
234
- if (currentPlayer.value) {
235
- currentPlayer.value.currentTime = time
236
- }
237
- }
238
-
239
- const toggleShuffle = () => {
240
- shuffle.value = !shuffle.value
241
- }
242
-
243
- const toggleRepeat = () => {
244
- const modes = ['none', 'all', 'one']
245
- const currentIndex = modes.indexOf(repeat.value)
246
- repeat.value = modes[(currentIndex + 1) % modes.length]
247
- }
248
-
249
- const onMediaLoaded = () => {
250
- if (currentPlayer.value) {
251
- duration.value = currentPlayer.value.duration || 0
252
- }
253
- }
254
-
255
- const onTimeUpdate = () => {
256
- if (currentPlayer.value) {
257
- currentTime.value = currentPlayer.value.currentTime || 0
258
- }
259
- }
260
-
261
- const onMediaEnded = () => {
262
- isPlaying.value = false
263
-
264
- if (repeat.value === 'one') {
265
- currentPlayer.value.currentTime = 0
266
- currentPlayer.value.play()
267
- isPlaying.value = true
268
- } else {
269
- nextTrack()
270
- }
271
- }
272
-
273
- const formatTime = (seconds) => {
274
- if (!seconds || isNaN(seconds)) return '0:00'
275
- const mins = Math.floor(seconds / 60)
276
- const secs = Math.floor(seconds % 60)
277
- return `${mins}:${secs.toString().padStart(2, '0')}`
278
- }
279
-
280
- onMounted(() => {
281
- if (props.autoplay && currentMedia.value) {
282
- nextTick(() => {
283
- togglePlayPause()
284
- })
285
- }
286
- })
287
- </script>
288
-
289
- <style scoped>
290
- .media-player {
291
- max-width: 100%;
292
- }
293
-
294
- .audio-player {
295
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
296
- color: white;
297
- }
298
-
299
- .playlist-list {
300
- background-color: rgba(0, 0, 0, 0.02);
301
- }
302
- </style>