| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <div> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     <BaseDialog | 
					
						
							|  |  |  |       v-model="dialog" | 
					
						
							|  |  |  |       :title="$t('recipe-share.share-recipe')" | 
					
						
							|  |  |  |       :icon="$globals.icons.link" | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |       <v-card-text> | 
					
						
							|  |  |  |         <v-menu | 
					
						
							|  |  |  |           v-model="datePickerMenu" | 
					
						
							|  |  |  |           :close-on-content-click="false" | 
					
						
							|  |  |  |           transition="scale-transition" | 
					
						
							|  |  |  |           offset-y | 
					
						
							|  |  |  |           max-width="290px" | 
					
						
							|  |  |  |           min-width="auto" | 
					
						
							|  |  |  |         > | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  |           <template #activator="{ props: activatorProps }"> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |             <v-text-field | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               v-model="expirationDateString" | 
					
						
							| 
									
										
										
										
											2022-08-10 07:12:45 +02:00
										 |  |  |               :label="$t('recipe-share.expiration-date')" | 
					
						
							|  |  |  |               :hint="$t('recipe-share.default-30-days')" | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |               persistent-hint | 
					
						
							|  |  |  |               :prepend-icon="$globals.icons.calendar" | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  |               v-bind="activatorProps" | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |               readonly | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             /> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |           </template> | 
					
						
							| 
									
										
										
										
											2024-03-12 17:46:34 -05:00
										 |  |  |           <v-date-picker | 
					
						
							|  |  |  |             v-model="expirationDate" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             hide-header | 
					
						
							| 
									
										
										
										
											2024-03-12 17:46:34 -05:00
										 |  |  |             :first-day-of-week="firstDayOfWeek" | 
					
						
							|  |  |  |             :local="$i18n.locale" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             @update:model-value="datePickerMenu = false" | 
					
						
							| 
									
										
										
										
											2024-03-12 17:46:34 -05:00
										 |  |  |           /> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |         </v-menu> | 
					
						
							|  |  |  |       </v-card-text> | 
					
						
							|  |  |  |       <v-card-actions class="justify-end"> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         <BaseButton | 
					
						
							|  |  |  |           size="small" | 
					
						
							|  |  |  |           @click="createNewToken" | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           {{ $t("general.new") }} | 
					
						
							|  |  |  |         </BaseButton> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |       </v-card-actions> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       <v-list-item | 
					
						
							|  |  |  |         v-for="token in tokens" | 
					
						
							|  |  |  |         :key="token.id" | 
					
						
							|  |  |  |         class="px-2" | 
					
						
							|  |  |  |         style="padding-top: 8px; padding-bottom: 8px;" | 
					
						
							|  |  |  |         @click="shareRecipe(token.id)" | 
					
						
							|  |  |  |       > | 
					
						
							|  |  |  |         <div class="d-flex align-center" style="width: 100%;"> | 
					
						
							|  |  |  |           <v-avatar color="grey"> | 
					
						
							|  |  |  |             <v-icon> | 
					
						
							|  |  |  |               {{ $globals.icons.link }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							|  |  |  |           </v-avatar> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <div class="pl-3 flex-grow-1"> | 
					
						
							|  |  |  |             <v-list-item-title> | 
					
						
							|  |  |  |               {{ $t("recipe-share.expires-at") }} | 
					
						
							|  |  |  |             </v-list-item-title> | 
					
						
							|  |  |  |             <v-list-item-subtitle> | 
					
						
							|  |  |  |               {{ $d(new Date(token.expiresAt!), "long") }} | 
					
						
							|  |  |  |             </v-list-item-subtitle> | 
					
						
							|  |  |  |           </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <v-btn | 
					
						
							|  |  |  |             icon | 
					
						
							|  |  |  |             variant="text" | 
					
						
							|  |  |  |             class="ml-2" | 
					
						
							|  |  |  |             @click.stop="deleteToken(token.id)" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon color="error-lighten-1"> | 
					
						
							|  |  |  |               {{ $globals.icons.delete }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |           </v-btn> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <v-btn | 
					
						
							|  |  |  |             icon | 
					
						
							|  |  |  |             variant="text" | 
					
						
							|  |  |  |             class="ml-2" | 
					
						
							|  |  |  |             @click.stop="copyTokenLink(token.id)" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon color="info-lighten-1"> | 
					
						
							|  |  |  |               {{ $globals.icons.contentCopy }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |           </v-btn> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |       </v-list-item> | 
					
						
							|  |  |  |     </BaseDialog> | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | <script setup lang="ts"> | 
					
						
							| 
									
										
										
										
											2022-05-25 10:14:24 -08:00
										 |  |  | import { useClipboard, useShare, whenever } from "@vueuse/core"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { RecipeShareToken } from "~/lib/api/types/recipe"; | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | import { useUserApi } from "~/composables/api"; | 
					
						
							| 
									
										
										
										
											2024-08-22 10:14:32 -05:00
										 |  |  | import { useHouseholdSelf } from "~/composables/use-households"; | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | import { alert } from "~/composables/use-toast"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | interface Props { | 
					
						
							|  |  |  |   recipeId: string; | 
					
						
							|  |  |  |   name: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | const props = defineProps<Props>(); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | const dialog = defineModel<boolean>({ default: false }); | 
					
						
							| 
									
										
										
										
											2024-03-12 17:46:34 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | const datePickerMenu = ref(false); | 
					
						
							|  |  |  | const expirationDate = ref(new Date(Date.now() - new Date().getTimezoneOffset() * 60000)); | 
					
						
							|  |  |  | const tokens = ref<RecipeShareToken[]>([]); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | const expirationDateString = computed(() => { | 
					
						
							|  |  |  |   return expirationDate.value.toISOString().substring(0, 10); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | whenever( | 
					
						
							|  |  |  |   () => dialog.value, | 
					
						
							|  |  |  |   () => { | 
					
						
							|  |  |  |     // Set expiration date to today + 30 Days
 | 
					
						
							|  |  |  |     const today = new Date(); | 
					
						
							|  |  |  |     expirationDate.value = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000); | 
					
						
							|  |  |  |     refreshTokens(); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | const i18n = useI18n(); | 
					
						
							|  |  |  | const $auth = useMealieAuth(); | 
					
						
							|  |  |  | const { household } = useHouseholdSelf(); | 
					
						
							|  |  |  | const route = useRoute(); | 
					
						
							|  |  |  | const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | const firstDayOfWeek = computed(() => { | 
					
						
							|  |  |  |   return household.value?.preferences?.firstDayOfWeek || 0; | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  | // ============================================================
 | 
					
						
							|  |  |  | // Token Actions
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const userApi = useUserApi(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function createNewToken() { | 
					
						
							|  |  |  |   // Convert expiration date to timestamp
 | 
					
						
							|  |  |  |   const { data } = await userApi.recipes.share.createOne({ | 
					
						
							|  |  |  |     recipeId: props.recipeId, | 
					
						
							|  |  |  |     expiresAt: expirationDate.value.toISOString(), | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (data) { | 
					
						
							|  |  |  |     tokens.value.push(data); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function deleteToken(id: string) { | 
					
						
							|  |  |  |   await userApi.recipes.share.deleteOne(id); | 
					
						
							|  |  |  |   tokens.value = tokens.value.filter(token => token.id !== id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function refreshTokens() { | 
					
						
							|  |  |  |   const { data } = await userApi.recipes.share.getAll(1, -1, { recipe_id: props.recipeId }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (data) { | 
					
						
							|  |  |  |     // @ts-expect-error - TODO: This routes doesn't have pagination, but the type are mismatched.
 | 
					
						
							|  |  |  |     tokens.value = data ?? []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const { share, isSupported: shareIsSupported } = useShare(); | 
					
						
							|  |  |  | const { copy, copied, isSupported } = useClipboard(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getRecipeText() { | 
					
						
							|  |  |  |   return i18n.t("recipe.share-recipe-message", [props.name]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getTokenLink(token: string) { | 
					
						
							|  |  |  |   return `${window.location.origin}/g/${groupSlug.value}/shared/r/${token}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function copyTokenLink(token: string) { | 
					
						
							|  |  |  |   if (isSupported.value) { | 
					
						
							|  |  |  |     await copy(getTokenLink(token)); | 
					
						
							|  |  |  |     if (copied.value) { | 
					
						
							|  |  |  |       alert.success(i18n.t("recipe-share.recipe-link-copied-message") as string); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  |     else { | 
					
						
							|  |  |  |       alert.error(i18n.t("general.clipboard-copy-failure") as string); | 
					
						
							| 
									
										
										
										
											2021-12-05 11:55:46 -09:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-07-30 20:37:02 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     alert.error(i18n.t("general.clipboard-not-supported") as string); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function shareRecipe(token: string) { | 
					
						
							|  |  |  |   if (shareIsSupported) { | 
					
						
							|  |  |  |     share({ | 
					
						
							|  |  |  |       title: props.name, | 
					
						
							|  |  |  |       url: getTokenLink(token), | 
					
						
							|  |  |  |       text: getRecipeText() as string, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     await copyTokenLink(token); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-01-16 03:38:11 +01:00
										 |  |  | </script> |