import { __ } from '@wordpress/i18n'; import { external, Icon } from '@wordpress/icons'; export const Support = ({ height }) => { if (!window.extHelpCenterData?.supportUrl) { return
; } return ( ); }; import apiFetch from '@wordpress/api-fetch'; import { safeParseJson } from '@shared/lib/parsing'; import { create } from 'zustand'; import { devtools, persist, createJSONStorage } from 'zustand/middleware'; const key = 'extendify-help-center-tour-progress'; const startingState = { currentTour: null, currentStep: undefined, preparingStep: undefined, progress: [], // initialize the state with default values ...(safeParseJson(window.extHelpCenterData.userData.tourData)?.state ?? {}), }; const state = (set, get) => ({ ...startingState, startTour: async (tourData) => { const { trackTourProgress, updateProgress, getStepData, onTourPage } = get(); if (onTourPage(tourData?.settings?.startFrom)) { await tourData?.onStart?.(tourData); tourData.steps = tourData.steps?.filter( // Filter out steps that define a condition (s) => s?.showOnlyIf?.() || s?.showOnlyIf?.() === undefined, ) || []; await getStepData(0, tourData)?.events?.beforeAttach?.(tourData); } set({ currentTour: tourData, currentStep: 0, preparingStep: undefined }); // Increment the opened count const tour = trackTourProgress(tourData.id); updateProgress(tour.id, { openedCount: Number(tour.openedCount) + 1, lastAction: 'started', }); }, onTourPage: (startFrom = null) => { const url = window.location.href; if (startFrom?.includes(url)) return true; const { currentTour } = get(); return currentTour?.settings?.startFrom?.includes(url); }, completeCurrentTour: async () => { const { currentTour, wasCompleted, findTourProgress, updateProgress } = get(); const tour = findTourProgress(currentTour?.id); if (!tour?.id) return; // if already completed, don't update the completedAt if (!wasCompleted(tour.id)) { updateProgress(tour.id, { completedAt: new Date().toISOString(), lastAction: 'completed', }); } // Track how many times it was completed updateProgress(tour.id, { completedCount: Number(tour.completedCount) + 1, lastAction: 'completed', }); await currentTour?.onDetach?.(); await currentTour?.onFinish?.(); set({ currentTour: null, currentStep: undefined }); // fire an event to update the site assistant tour status in assistant code base. if (tour?.id === 'site-assistant-tour') { window.dispatchEvent( new CustomEvent('extendify-assist:is-tour-finished', { detail: { isFinished: true }, }), ); } }, closeCurrentTour: async (lastAction) => { const { currentTour, findTourProgress, updateProgress } = get(); const tour = findTourProgress(currentTour?.id); if (!tour?.id) return; const additional = {}; if (['redirected'].includes(lastAction)) { return updateProgress(tour?.id, { lastAction }); } if (['closed-by-caught-error'].includes(lastAction)) { return updateProgress(tour?.id, { lastAction, errored: true }); } if (lastAction === 'closed-manually') { additional.closedManuallyCount = Number(tour.closedManuallyCount) + 1; } await currentTour?.onDetach?.(); await currentTour?.onFinish?.(); updateProgress(tour?.id, { lastAction, ...additional }); set({ currentTour: null, currentStep: undefined, preparingStep: undefined, }); }, findTourProgress: (tourId) => get().progress.find((tour) => tour.id === tourId), wasCompleted: (tourId) => get().findTourProgress(tourId)?.completedAt, wasOpened: (tourId) => Number(get().findTourProgress(tourId)?.openedCount ?? 0) > 0, isSeen: (tourId) => get().findTourProgress(tourId)?.firstSeenAt, trackTourProgress: (tourId) => { const { findTourProgress } = get(); // If we are already tracking it, return that if (findTourProgress(tourId)) { return findTourProgress(tourId); } set((state) => ({ progress: [ ...state.progress, { id: tourId, firstSeenAt: new Date().toISOString(), updatedAt: new Date().toISOString(), completedAt: null, lastAction: 'init', currentStep: 0, openedCount: 0, closedManuallyCount: 0, completedCount: 0, errored: false, }, ], })); return findTourProgress(tourId); }, updateProgress: (tourId, update) => { const lastAction = update?.lastAction ?? 'unknown'; set((state) => { const progress = state.progress.map((tour) => { if (tour.id === tourId) { return { ...tour, ...update, lastAction, updatedAt: new Date().toISOString(), }; } return tour; }); return { progress }; }); }, getStepData: (step, tour = get().currentTour) => tour?.steps?.[step] ?? {}, hasNextStep: () => { if (!get().currentTour) return false; return Number(get().currentStep) < get().currentTour.steps.length - 1; }, nextStep: async () => { const { currentTour, goToStep, updateProgress, currentStep } = get(); const step = Number(currentStep) + 1; await goToStep(step); updateProgress(currentTour.id, { currentStep: step, lastAction: 'next', }); }, hasPreviousStep: () => { if (!get().currentTour) return false; return Number(get().currentStep) > 0; }, prevStep: async () => { const { currentTour, goToStep, updateProgress, currentStep } = get(); const step = currentStep - 1; await goToStep(step); updateProgress(currentTour.id, { currentStep: step, lastAction: 'prev', }); }, goToStep: async (step) => { const { currentTour, updateProgress, closeCurrentTour, getStepData } = get(); const tour = currentTour; // Check that the step is valid if (step < 0 || step > tour.steps.length - 1) { closeCurrentTour('closed-by-caught-error'); return; } updateProgress(tour.id, { currentStep: step, lastAction: `go-to-step-${step}`, }); const events = getStepData(step)?.events; if (events?.beforeAttach) { set(() => ({ preparingStep: step })); // Make sure the preparing animation runs at least 300ms await Promise.allSettled([ events.beforeAttach?.(tour), new Promise((resolve) => setTimeout(resolve, 300)), ]); set(() => ({ preparingStep: undefined })); } set(() => ({ currentStep: step })); }, }); const path = '/extendify/v1/help-center/tour-data'; const storage = { getItem: async () => await apiFetch({ path }), setItem: async (_name, state) => await apiFetch({ path, method: 'POST', data: { state } }), }; export const useTourStore = create( persist(devtools(state, { name: 'Extendify Tour Progress' }), { name: key, storage: createJSONStorage(() => storage), skipHydration: true, partialize: (state) => { // return without currentTour or currentStep // eslint-disable-next-line no-unused-vars const { currentTour, currentStep, preparingStep, ...newState } = state; return newState; }, }), ); import BarChart from './BarChart'; import Checkmark from './Checkmark'; import Design from './Design'; import Donate from './Donate'; import LeftCaret from './LeftCaret'; import Logo from './Logo'; import Monetization from './Monetization'; import OpenEnvelope from './OpenEnvelope'; import Pencil from './Pencil'; import Planner from './Planner'; import PreviewIcon from './PreviewIcon'; import PriceTag from './PriceTag'; import Radio from './Radio'; import RefreshIcon from './RefreshIcon'; import RightCaret from './RightCaret'; import School from './School'; import SearchIcon from './SearchIcon'; import Shop from './Shop'; import Speech from './Speech'; import Spinner from './Spinner'; import SpinnerIcon from './SpinnerIcon'; import Ticket from './Ticket'; export { BarChart, Checkmark, Design, Donate, LeftCaret, Logo, Monetization, OpenEnvelope, Pencil, Planner, PreviewIcon, PriceTag, Radio, School, RefreshIcon, RightCaret, SearchIcon, Shop, Speech, Spinner, SpinnerIcon, Ticket, }; {"id":2,"date":"2024-07-05T19:34:33","date_gmt":"2024-07-05T19:34:33","guid":{"rendered":"https:\/\/nossamidia.net.br\/?page_id=2"},"modified":"2024-07-05T19:34:33","modified_gmt":"2024-07-05T19:34:33","slug":"sample-page","status":"publish","type":"page","link":"https:\/\/nossamidia.net.br\/sample-page\/","title":{"rendered":"Sample Page"},"content":{"rendered":"This is an example page. It’s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:<\/p>\n\n\n\n
Hi there! I’m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin’ caught in the rain.)<\/p><\/blockquote>\n\n\n\n
…or something like this:<\/p>\n\n\n\n
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.<\/p><\/blockquote>\n\n\n\n