@bikdotai/bik-component-library 0.0.811-beta.3 → 0.0.812-beta.0
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/cjs/components/feature-announcements/hooks/useFeatureAnnouncements.js +1 -1
- package/dist/cjs/components/feature-announcements/hooks/useFeatureAnnouncements.js.map +1 -1
- package/dist/cjs/editor/serializers/toLiveChatText.js +1 -1
- package/dist/cjs/editor/serializers/toLiveChatText.js.map +1 -1
- package/dist/cjs/editor/serializers/toWhatsAppText.js +1 -1
- package/dist/cjs/editor/serializers/toWhatsAppText.js.map +1 -1
- package/dist/esm/components/feature-announcements/hooks/useFeatureAnnouncements.js +1 -1
- package/dist/esm/components/feature-announcements/hooks/useFeatureAnnouncements.js.map +1 -1
- package/dist/esm/editor/serializers/toLiveChatText.js +1 -1
- package/dist/esm/editor/serializers/toLiveChatText.js.map +1 -1
- package/dist/esm/editor/serializers/toWhatsAppText.js +1 -1
- package/dist/esm/editor/serializers/toWhatsAppText.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../../_virtual/_tslib.js"),t=require("react"),r=require("../constants/animations.js"),a=require("../constants/localStorageKeys.js");exports.useFeatureAnnouncements=i=>{let{fetchVisibleFeatures:o,getStoreFeatureProgress:n,fetchFeatureById:l,markFeatureAsViewedForStore:u,isFeatureApplicableToCurrentPage:s,module:c,router:d}=i;var f;const p=(()=>{const[e,r]=t.useState(null);return t.useEffect((()=>{if("undefined"!=typeof window){let e=null;try{if(e=localStorage.getItem(a.LOCAL_STORAGE_KEYS.STORE_ID)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.BIK_STORE_ID)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.CURRENT_STORE)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.STORE),!e){const t=localStorage.getItem(a.LOCAL_STORAGE_KEYS.CURRENT_USER);t&&(e=JSON.parse(t).storeId)}e||(e=new URLSearchParams(window.location.search).get(a.LOCAL_STORAGE_KEYS.QUERY_PARAM))}catch(e){}r(e)}}),[]),e})(),[S,_]=t.useState([]),[A,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../../_virtual/_tslib.js"),t=require("react"),r=require("../constants/animations.js"),a=require("../constants/localStorageKeys.js");exports.useFeatureAnnouncements=i=>{let{fetchVisibleFeatures:o,getStoreFeatureProgress:n,fetchFeatureById:l,markFeatureAsViewedForStore:u,isFeatureApplicableToCurrentPage:s,module:c,router:d}=i;var f;const p=(()=>{const[e,r]=t.useState(null);return t.useEffect((()=>{if("undefined"!=typeof window){let e=null;try{if(e=localStorage.getItem(a.LOCAL_STORAGE_KEYS.STORE_ID)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.BIK_STORE_ID)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.CURRENT_STORE)||localStorage.getItem(a.LOCAL_STORAGE_KEYS.STORE),!e){const t=localStorage.getItem(a.LOCAL_STORAGE_KEYS.CURRENT_USER);t&&(e=JSON.parse(t).storeId)}e||(e=new URLSearchParams(window.location.search).get(a.LOCAL_STORAGE_KEYS.QUERY_PARAM))}catch(e){}r(e)}}),[]),e})(),[S,_]=t.useState([]),[A,g]=t.useState([]),[E,v]=t.useState([]),[y,R]=t.useState(!1),[T,U]=t.useState(null),D=t.useCallback((()=>e.__awaiter(void 0,void 0,void 0,(function*(){if(p){R(!0),U(null);try{const[e,t]=yield o(c);if(e||!t)return void U("Failed to fetch features");const[a,i]=yield n(p),l=new Date,u="undefined"!=typeof window?window.location.pathname:"",d=e=>{if(!e.expirationDate)return!0;let t;return"object"==typeof e.expirationDate&&null!==e.expirationDate&&("seconds"in e.expirationDate||"nanoseconds"in e.expirationDate)?t=new Date(1e3*e.expirationDate.seconds):"string"==typeof e.expirationDate?t=new Date(e.expirationDate):e.expirationDate instanceof Date&&(t=e.expirationDate),!(!t||isNaN(t.getTime()))&&l<t},f=e=>{if("Major"===e.featureUpdateType){let t=[];return Array.isArray(e.pageUrls)?t=e.pageUrls:"object"==typeof e.pageUrls&&null!==e.pageUrls&&"length"in e.pageUrls&&(t=Array.from(e.pageUrls)),0===t.length||t.some((e=>{if(e===u)return!0;const t=e.replace(/\[([^\]]+)\]/g,"[^/]+").replace(/\//g,"\\/");return new RegExp(`^${t}$`).test(u)}))}return!0},S=t.filter((e=>{var t;const r=(null===(t=null==i?void 0:i.viewedFeatures)||void 0===t?void 0:t[e.id])||!1,a=e.archived||!1,o=s(e),n=d(e),l=f(e);return!r&&!a&&o&&n&&l})),A=S.filter((e=>"Major"===e.featureUpdateType)),E=S.filter((e=>"Minor"===e.featureUpdateType));_(S),g(A),setTimeout((()=>{v(E)}),r.ANIMATION_TIMING.MINOR_POPUP_DELAY)}catch(e){U("An unexpected error occurred")}finally{R(!1)}}}))),[p,o,n,s,c]),O=t.useCallback((t=>e.__awaiter(void 0,void 0,void 0,(function*(){R(!0),U(null);try{const[e,r]=yield l(t);if(e||!r)return void U("Failed to fetch feature by ID");"Major"===r.featureUpdateType?g([r]):v([r]),_([r])}catch(e){U("An unexpected error occurred while fetching by ID")}finally{R(!1)}}))),[l]),w=t.useCallback((t=>e.__awaiter(void 0,void 0,void 0,(function*(){if(!p)return;const[e]=yield u(p,t);_((e=>e.filter((e=>e.id!==t)))),g((e=>e.filter((e=>e.id!==t)))),v((e=>e.filter((e=>e.id!==t))))}))),[p,u]);return t.useEffect((()=>{var e;const t=null===(e=null==d?void 0:d.query)||void 0===e?void 0:e[a.QUERY_PARAMS.FEATURE_ID];t&&d?O(t):D()}),[null==d?void 0:d.pathname,null===(f=null==d?void 0:d.query)||void 0===f?void 0:f[a.QUERY_PARAMS.FEATURE_ID],p,D,O,d]),{features:S,majorUpdateFeatures:A,minorUpdateFeatures:E,isLoading:y,error:T,markFeatureAsViewed:w,refetch:D}};
|
|
2
2
|
//# sourceMappingURL=useFeatureAnnouncements.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFeatureAnnouncements.js","sources":["../../../../../src/components/feature-announcements/hooks/useFeatureAnnouncements.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport {\n\tANIMATION_TIMING,\n\tLOCAL_STORAGE_KEYS,\n\tQUERY_PARAMS,\n} from '../constants';\nimport {\n\tFeatureAnnouncement,\n\tFeatureAnnouncementRepository,\n\tRouterProps,\n} from '../types';\n\nconst useStoreId = () => {\n\tconst [storeId, setStoreId] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (typeof window !== 'undefined') {\n\t\t\tlet storedStoreId: string | null = null;\n\n\t\t\ttry {\n\t\t\t\tstoredStoreId =\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.BIK_STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.CURRENT_STORE) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE);\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tconst currentUser = localStorage.getItem(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.CURRENT_USER,\n\t\t\t\t\t);\n\t\t\t\t\tif (currentUser) {\n\t\t\t\t\t\tconst parsed = JSON.parse(currentUser);\n\t\t\t\t\t\tstoredStoreId = parsed.storeId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tstoredStoreId = new URLSearchParams(window.location.search).get(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.QUERY_PARAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t// Failed to get store ID\n\t\t\t}\n\t\t\tsetStoreId(storedStoreId);\n\t\t}\n\t}, []);\n\n\treturn storeId;\n};\n\nexport const useFeatureAnnouncements = ({\n\tfetchVisibleFeatures,\n\tgetStoreFeatureProgress,\n\tfetchFeatureById,\n\tmarkFeatureAsViewedForStore,\n\tisFeatureApplicableToCurrentPage,\n\tmodule,\n\trouter,\n}: FeatureAnnouncementRepository & {\n\tmodule?: string;\n\trouter?: RouterProps;\n}) => {\n\tconst storeId = useStoreId();\n\tconst [features, setFeatures] = useState<FeatureAnnouncement[]>([]);\n\tconst [majorUpdateFeatures, setMajorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [minorUpdateFeatures, setMinorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst fetchFeatures = useCallback(async () => {\n\t\tif (!storeId) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsLoading(true);\n\t\tsetError(null);\n\n\t\ttry {\n\t\t\tconst [featuresError, allFeatures] = await fetchVisibleFeatures(module);\n\n\t\t\tif (featuresError || !allFeatures) {\n\t\t\t\tsetError('Failed to fetch features');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [progressError, userProgress] = await getStoreFeatureProgress(\n\t\t\t\tstoreId,\n\t\t\t);\n\n\t\t\tif (progressError) {\n\t\t\t\t// Progress fetch failed, treating all features as new\n\t\t\t}\n\n\t\t\tconst now = new Date();\n\t\t\tconst currentRoute =\n\t\t\t\ttypeof window !== 'undefined' ? window.location.pathname : '';\n\n\t\t\tconst isNotExpired = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (!feature.expirationDate) return true;\n\t\t\t\tlet expiry: Date | undefined;\n\t\t\t\t// Firestore timestamp object\n\t\t\t\tif (\n\t\t\t\t\ttypeof feature.expirationDate === 'object' &&\n\t\t\t\t\tfeature.expirationDate !== null &&\n\t\t\t\t\t('seconds' in feature.expirationDate ||\n\t\t\t\t\t\t'nanoseconds' in feature.expirationDate)\n\t\t\t\t) {\n\t\t\t\t\t// Firestore timestamp: { seconds: number, nanoseconds?: number }\n\t\t\t\t\texpiry = new Date((feature.expirationDate as any).seconds * 1000);\n\t\t\t\t} else if (typeof feature.expirationDate === 'string') {\n\t\t\t\t\t// String date\n\t\t\t\t\texpiry = new Date(feature.expirationDate);\n\t\t\t\t} else if (feature.expirationDate instanceof Date) {\n\t\t\t\t\texpiry = feature.expirationDate;\n\t\t\t\t}\n\t\t\t\tif (!expiry || isNaN(expiry.getTime())) return false;\n\t\t\t\treturn now < expiry;\n\t\t\t};\n\n\t\t\tconst isOnCorrectPage = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\t// Accept both array and object (Firestore array)\n\t\t\t\t\tlet urls: string[] = [];\n\t\t\t\t\tif (Array.isArray(feature.pageUrls)) {\n\t\t\t\t\t\turls = feature.pageUrls;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\ttypeof feature.pageUrls === 'object' &&\n\t\t\t\t\t\tfeature.pageUrls !== null &&\n\t\t\t\t\t\t'length' in feature.pageUrls\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Firestore array\n\t\t\t\t\t\turls = Array.from(feature.pageUrls as any);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for exact match or dynamic route pattern match\n\t\t\t\t\treturn urls.some((url) => {\n\t\t\t\t\t\t// Exact match\n\t\t\t\t\t\tif (url === currentRoute) return true;\n\n\t\t\t\t\t\t// Dynamic route matching (e.g., /campaign/details/[broadcastId])\n\t\t\t\t\t\t// Convert Next.js route pattern to regex\n\t\t\t\t\t\tconst pattern = url\n\t\t\t\t\t\t\t.replace(/\\[([^\\]]+)\\]/g, '[^/]+') // Replace [param] with [^/]+\n\t\t\t\t\t\t\t.replace(/\\//g, '\\\\/'); // Escape forward slashes\n\n\t\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`);\n\t\t\t\t\t\treturn regex.test(currentRoute);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst unviewedFeatures = allFeatures.filter((feature) => {\n\t\t\t\tconst isViewed = userProgress?.viewedFeatures?.[feature.id] || false;\n\t\t\t\tconst isArchived = feature.archived || false;\n\t\t\t\tconst isApplicable = isFeatureApplicableToCurrentPage(feature);\n\t\t\t\tconst notExpired = isNotExpired(feature);\n\t\t\t\tconst onCorrectPage = isOnCorrectPage(feature);\n\n\t\t\t\tconst shouldShow =\n\t\t\t\t\t!isViewed &&\n\t\t\t\t\t!isArchived &&\n\t\t\t\t\tisApplicable &&\n\t\t\t\t\tnotExpired &&\n\t\t\t\t\tonCorrectPage;\n\n\t\t\t\treturn shouldShow;\n\t\t\t});\n\n\t\t\t// Separate major and minor updates\n\t\t\tconst majorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Major',\n\t\t\t);\n\t\t\tconst minorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Minor',\n\t\t\t);\n\n\t\t\tsetFeatures(unviewedFeatures);\n\t\t\tsetMajorUpdateFeatures(majorFeatures);\n\t\t\t// Delay minor popups by 2 seconds\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetMinorUpdateFeatures(minorFeatures);\n\t\t\t}, ANIMATION_TIMING.MINOR_POPUP_DELAY);\n\t\t} catch (err) {\n\t\t\tsetError('An unexpected error occurred');\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t}, [\n\t\tstoreId,\n\t\tfetchVisibleFeatures,\n\t\tgetStoreFeatureProgress,\n\t\tisFeatureApplicableToCurrentPage,\n\t\tmodule,\n\t]);\n\n\tconst fetchFeatureByIdCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst [error, feature] = await fetchFeatureById(featureId);\n\t\t\t\tif (error || !feature) {\n\t\t\t\t\tsetError('Failed to fetch feature by ID');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\tsetMajorUpdateFeatures([feature]);\n\t\t\t\t} else {\n\t\t\t\t\tsetMinorUpdateFeatures([feature]);\n\t\t\t\t}\n\t\t\t\tsetFeatures([feature]);\n\t\t\t} catch (err) {\n\t\t\t\tsetError('An unexpected error occurred while fetching by ID');\n\t\t\t} finally {\n\t\t\t\tsetIsLoading(false);\n\t\t\t}\n\t\t},\n\t\t[fetchFeatureById],\n\t);\n\n\tconst markFeatureAsViewedCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tif (!storeId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [error] = await markFeatureAsViewedForStore(storeId, featureId);\n\n\t\t\tif (!error) {\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Failed to mark feature as viewed, removing from local state anyway\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\t[storeId, markFeatureAsViewedForStore],\n\t);\n\n\tuseEffect(() => {\n\t\tconst featureIdFromQuery = router?.query?.[\n\t\t\tQUERY_PARAMS.FEATURE_ID\n\t\t] as string;\n\n\t\t// In test mode with featureId, ONLY fetch that specific feature\n\t\tif (featureIdFromQuery && router) {\n\t\t\tfetchFeatureByIdCallback(featureIdFromQuery);\n\t\t\treturn; // Don't run fetchFeatures\n\t\t}\n\n\t\t// Normal mode: fetch all features\n\t\tfetchFeatures();\n\t}, [\n\t\trouter?.pathname,\n\t\trouter?.query?.[QUERY_PARAMS.FEATURE_ID],\n\t\tstoreId,\n\t\tfetchFeatures,\n\t\tfetchFeatureByIdCallback,\n\t\trouter,\n\t]);\n\n\treturn {\n\t\tfeatures,\n\t\tmajorUpdateFeatures,\n\t\tminorUpdateFeatures,\n\t\tisLoading,\n\t\terror,\n\t\tmarkFeatureAsViewed: markFeatureAsViewedCallback,\n\t\trefetch: fetchFeatures,\n\t};\n};\n"],"names":["_ref","fetchVisibleFeatures","getStoreFeatureProgress","fetchFeatureById","markFeatureAsViewedForStore","isFeatureApplicableToCurrentPage","module","router","storeId","useStoreId","setStoreId","useState","useEffect","window","storedStoreId","localStorage","getItem","LOCAL_STORAGE_KEYS","STORE_ID","BIK_STORE_ID","CURRENT_STORE","STORE","currentUser","CURRENT_USER","JSON","parse","URLSearchParams","location","search","get","QUERY_PARAM","e","features","setFeatures","majorUpdateFeatures","setMajorUpdateFeatures","minorUpdateFeatures","setMinorUpdateFeatures","isLoading","setIsLoading","error","setError","fetchFeatures","useCallback","__awaiter","featuresError","allFeatures","progressError","userProgress","now","Date","currentRoute","pathname","isNotExpired","feature","expirationDate","expiry","seconds","isNaN","getTime","isOnCorrectPage","featureUpdateType","urls","Array","isArray","pageUrls","from","some","url","pattern","replace","RegExp","test","unviewedFeatures","filter","isViewed","viewedFeatures","id","isArchived","archived","isApplicable","notExpired","onCorrectPage","majorFeatures","f","minorFeatures","setTimeout","ANIMATION_TIMING","MINOR_POPUP_DELAY","err","fetchFeatureByIdCallback","featureId","markFeatureAsViewedCallback","prev","featureIdFromQuery","_a","query","QUERY_PARAMS","FEATURE_ID","markFeatureAsViewed","refetch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFeatureAnnouncements.js","sources":["../../../../../src/components/feature-announcements/hooks/useFeatureAnnouncements.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport {\n\tANIMATION_TIMING,\n\tLOCAL_STORAGE_KEYS,\n\tQUERY_PARAMS,\n} from '../constants';\nimport {\n\tFeatureAnnouncement,\n\tFeatureAnnouncementRepository,\n\tRouterProps,\n} from '../types';\n\nconst useStoreId = () => {\n\tconst [storeId, setStoreId] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (typeof window !== 'undefined') {\n\t\t\tlet storedStoreId: string | null = null;\n\n\t\t\ttry {\n\t\t\t\tstoredStoreId =\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.BIK_STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.CURRENT_STORE) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE);\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tconst currentUser = localStorage.getItem(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.CURRENT_USER,\n\t\t\t\t\t);\n\t\t\t\t\tif (currentUser) {\n\t\t\t\t\t\tconst parsed = JSON.parse(currentUser);\n\t\t\t\t\t\tstoredStoreId = parsed.storeId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tstoredStoreId = new URLSearchParams(window.location.search).get(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.QUERY_PARAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t// Failed to get store ID\n\t\t\t}\n\t\t\tsetStoreId(storedStoreId);\n\t\t}\n\t}, []);\n\n\treturn storeId;\n};\n\nexport const useFeatureAnnouncements = ({\n\tfetchVisibleFeatures,\n\tgetStoreFeatureProgress,\n\tfetchFeatureById,\n\tmarkFeatureAsViewedForStore,\n\tisFeatureApplicableToCurrentPage,\n\tmodule,\n\trouter,\n}: FeatureAnnouncementRepository & {\n\tmodule?: string;\n\trouter?: RouterProps;\n}) => {\n\tconst storeId = useStoreId();\n\tconst [features, setFeatures] = useState<FeatureAnnouncement[]>([]);\n\tconst [majorUpdateFeatures, setMajorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [minorUpdateFeatures, setMinorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst fetchFeatures = useCallback(async () => {\n\t\tif (!storeId) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsLoading(true);\n\t\tsetError(null);\n\n\t\ttry {\n\t\t\tconst [featuresError, allFeatures] = await fetchVisibleFeatures(module);\n\n\t\t\tif (featuresError || !allFeatures) {\n\t\t\t\tsetError('Failed to fetch features');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [progressError, userProgress] = await getStoreFeatureProgress(\n\t\t\t\tstoreId,\n\t\t\t);\n\n\t\t\tif (progressError) {\n\t\t\t\t// Progress fetch failed, treating all features as new\n\t\t\t}\n\n\t\t\tconst now = new Date();\n\t\t\tconst currentRoute =\n\t\t\t\ttypeof window !== 'undefined' ? window.location.pathname : '';\n\n\t\t\tconst isNotExpired = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (!feature.expirationDate) return true;\n\t\t\t\tlet expiry: Date | undefined;\n\t\t\t\t// Firestore timestamp object\n\t\t\t\tif (\n\t\t\t\t\ttypeof feature.expirationDate === 'object' &&\n\t\t\t\t\tfeature.expirationDate !== null &&\n\t\t\t\t\t('seconds' in feature.expirationDate ||\n\t\t\t\t\t\t'nanoseconds' in feature.expirationDate)\n\t\t\t\t) {\n\t\t\t\t\t// Firestore timestamp: { seconds: number, nanoseconds?: number }\n\t\t\t\t\texpiry = new Date((feature.expirationDate as any).seconds * 1000);\n\t\t\t\t} else if (typeof feature.expirationDate === 'string') {\n\t\t\t\t\t// String date\n\t\t\t\t\texpiry = new Date(feature.expirationDate);\n\t\t\t\t} else if (feature.expirationDate instanceof Date) {\n\t\t\t\t\texpiry = feature.expirationDate;\n\t\t\t\t}\n\t\t\t\tif (!expiry || isNaN(expiry.getTime())) return false;\n\t\t\t\treturn now < expiry;\n\t\t\t};\n\n\t\t\tconst isOnCorrectPage = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\t// Accept both array and object (Firestore array)\n\t\t\t\t\tlet urls: string[] = [];\n\t\t\t\t\tif (Array.isArray(feature.pageUrls)) {\n\t\t\t\t\t\turls = feature.pageUrls;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\ttypeof feature.pageUrls === 'object' &&\n\t\t\t\t\t\tfeature.pageUrls !== null &&\n\t\t\t\t\t\t'length' in feature.pageUrls\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Firestore array\n\t\t\t\t\t\turls = Array.from(feature.pageUrls as any);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Empty pageUrls means no restriction — show on all pages\n\t\t\t\t\tif (urls.length === 0) return true;\n\n\t\t\t\t\t// Check for exact match or dynamic route pattern match\n\t\t\t\t\treturn urls.some((url) => {\n\t\t\t\t\t\t// Exact match\n\t\t\t\t\t\tif (url === currentRoute) return true;\n\n\t\t\t\t\t\t// Dynamic route matching (e.g., /campaign/details/[broadcastId])\n\t\t\t\t\t\t// Convert Next.js route pattern to regex\n\t\t\t\t\t\tconst pattern = url\n\t\t\t\t\t\t\t.replace(/\\[([^\\]]+)\\]/g, '[^/]+') // Replace [param] with [^/]+\n\t\t\t\t\t\t\t.replace(/\\//g, '\\\\/'); // Escape forward slashes\n\n\t\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`);\n\t\t\t\t\t\treturn regex.test(currentRoute);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst unviewedFeatures = allFeatures.filter((feature) => {\n\t\t\t\tconst isViewed = userProgress?.viewedFeatures?.[feature.id] || false;\n\t\t\t\tconst isArchived = feature.archived || false;\n\t\t\t\tconst isApplicable = isFeatureApplicableToCurrentPage(feature);\n\t\t\t\tconst notExpired = isNotExpired(feature);\n\t\t\t\tconst onCorrectPage = isOnCorrectPage(feature);\n\n\t\t\t\tconst shouldShow =\n\t\t\t\t\t!isViewed &&\n\t\t\t\t\t!isArchived &&\n\t\t\t\t\tisApplicable &&\n\t\t\t\t\tnotExpired &&\n\t\t\t\t\tonCorrectPage;\n\n\t\t\t\treturn shouldShow;\n\t\t\t});\n\n\t\t\t// Separate major and minor updates\n\t\t\tconst majorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Major',\n\t\t\t);\n\t\t\tconst minorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Minor',\n\t\t\t);\n\n\t\t\tsetFeatures(unviewedFeatures);\n\t\t\tsetMajorUpdateFeatures(majorFeatures);\n\t\t\t// Delay minor popups by 2 seconds\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetMinorUpdateFeatures(minorFeatures);\n\t\t\t}, ANIMATION_TIMING.MINOR_POPUP_DELAY);\n\t\t} catch (err) {\n\t\t\tsetError('An unexpected error occurred');\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t}, [\n\t\tstoreId,\n\t\tfetchVisibleFeatures,\n\t\tgetStoreFeatureProgress,\n\t\tisFeatureApplicableToCurrentPage,\n\t\tmodule,\n\t]);\n\n\tconst fetchFeatureByIdCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst [error, feature] = await fetchFeatureById(featureId);\n\t\t\t\tif (error || !feature) {\n\t\t\t\t\tsetError('Failed to fetch feature by ID');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\tsetMajorUpdateFeatures([feature]);\n\t\t\t\t} else {\n\t\t\t\t\tsetMinorUpdateFeatures([feature]);\n\t\t\t\t}\n\t\t\t\tsetFeatures([feature]);\n\t\t\t} catch (err) {\n\t\t\t\tsetError('An unexpected error occurred while fetching by ID');\n\t\t\t} finally {\n\t\t\t\tsetIsLoading(false);\n\t\t\t}\n\t\t},\n\t\t[fetchFeatureById],\n\t);\n\n\tconst markFeatureAsViewedCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tif (!storeId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [error] = await markFeatureAsViewedForStore(storeId, featureId);\n\n\t\t\tif (!error) {\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Failed to mark feature as viewed, removing from local state anyway\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\t[storeId, markFeatureAsViewedForStore],\n\t);\n\n\tuseEffect(() => {\n\t\tconst featureIdFromQuery = router?.query?.[\n\t\t\tQUERY_PARAMS.FEATURE_ID\n\t\t] as string;\n\n\t\t// In test mode with featureId, ONLY fetch that specific feature\n\t\tif (featureIdFromQuery && router) {\n\t\t\tfetchFeatureByIdCallback(featureIdFromQuery);\n\t\t\treturn; // Don't run fetchFeatures\n\t\t}\n\n\t\t// Normal mode: fetch all features\n\t\tfetchFeatures();\n\t}, [\n\t\trouter?.pathname,\n\t\trouter?.query?.[QUERY_PARAMS.FEATURE_ID],\n\t\tstoreId,\n\t\tfetchFeatures,\n\t\tfetchFeatureByIdCallback,\n\t\trouter,\n\t]);\n\n\treturn {\n\t\tfeatures,\n\t\tmajorUpdateFeatures,\n\t\tminorUpdateFeatures,\n\t\tisLoading,\n\t\terror,\n\t\tmarkFeatureAsViewed: markFeatureAsViewedCallback,\n\t\trefetch: fetchFeatures,\n\t};\n};\n"],"names":["_ref","fetchVisibleFeatures","getStoreFeatureProgress","fetchFeatureById","markFeatureAsViewedForStore","isFeatureApplicableToCurrentPage","module","router","storeId","useStoreId","setStoreId","useState","useEffect","window","storedStoreId","localStorage","getItem","LOCAL_STORAGE_KEYS","STORE_ID","BIK_STORE_ID","CURRENT_STORE","STORE","currentUser","CURRENT_USER","JSON","parse","URLSearchParams","location","search","get","QUERY_PARAM","e","features","setFeatures","majorUpdateFeatures","setMajorUpdateFeatures","minorUpdateFeatures","setMinorUpdateFeatures","isLoading","setIsLoading","error","setError","fetchFeatures","useCallback","__awaiter","featuresError","allFeatures","progressError","userProgress","now","Date","currentRoute","pathname","isNotExpired","feature","expirationDate","expiry","seconds","isNaN","getTime","isOnCorrectPage","featureUpdateType","urls","Array","isArray","pageUrls","from","length","some","url","pattern","replace","RegExp","test","unviewedFeatures","filter","isViewed","viewedFeatures","id","isArchived","archived","isApplicable","notExpired","onCorrectPage","majorFeatures","f","minorFeatures","setTimeout","ANIMATION_TIMING","MINOR_POPUP_DELAY","err","fetchFeatureByIdCallback","featureId","markFeatureAsViewedCallback","prev","featureIdFromQuery","_a","query","QUERY_PARAMS","FEATURE_ID","markFeatureAsViewed","refetch"],"mappings":"0PAmDuCA,IAWlC,IAXmCC,qBACvCA,EAAoBC,wBACpBA,EAAuBC,iBACvBA,EAAgBC,4BAChBA,EAA2BC,iCAC3BA,EAAgCC,OAChCA,EAAMC,OACNA,GAIAP,QACA,MAAMQ,EAnDYC,MAClB,MAAOD,EAASE,GAAcC,EAAQA,SAAgB,MAmCtD,OAjCAC,EAAAA,WAAU,KACT,GAAsB,oBAAXC,OAAwB,CAClC,IAAIC,EAA+B,KAEnC,IAOC,GANAA,EACCC,aAAaC,QAAQC,EAAkBA,mBAACC,WACxCH,aAAaC,QAAQC,qBAAmBE,eACxCJ,aAAaC,QAAQC,EAAAA,mBAAmBG,gBACxCL,aAAaC,QAAQC,qBAAmBI,QAEpCP,EAAe,CACnB,MAAMQ,EAAcP,aAAaC,QAChCC,EAAkBA,mBAACM,cAEhBD,IAEHR,EADeU,KAAKC,MAAMH,GACHd,QAExB,CAEIM,IACJA,EAAgB,IAAIY,gBAAgBb,OAAOc,SAASC,QAAQC,IAC3DZ,qBAAmBa,aAGrB,CAAC,MAAOC,GACR,CAEDrB,EAAWI,EACX,IACC,IAEIN,CAAO,EAeEC,IACTuB,EAAUC,GAAetB,EAAQA,SAAwB,KACzDuB,EAAqBC,GAA0BxB,EAAQA,SAE5D,KACKyB,EAAqBC,GAA0B1B,EAAQA,SAE5D,KACK2B,EAAWC,GAAgB5B,EAAQA,UAAC,IACpC6B,EAAOC,GAAY9B,EAAQA,SAAgB,MAE5C+B,EAAgBC,EAAWA,aAAC,IAAWC,EAAAA,eAAA,OAAA,OAAA,GAAA,YAC5C,GAAKpC,EAAL,CAIA+B,GAAa,GACbE,EAAS,MAET,IACC,MAAOI,EAAeC,SAAqB7C,EAAqBK,GAEhE,GAAIuC,IAAkBC,EAErB,YADAL,EAAS,4BAIV,MAAOM,EAAeC,SAAsB9C,EAC3CM,GAOKyC,EAAM,IAAIC,KACVC,EACa,oBAAXtC,OAAyBA,OAAOc,SAASyB,SAAW,GAEtDC,EAAgBC,IACrB,IAAKA,EAAQC,eAAgB,OAAO,EACpC,IAAIC,EAgBJ,MAbmC,iBAA3BF,EAAQC,gBACY,OAA3BD,EAAQC,iBACP,YAAaD,EAAQC,gBACrB,gBAAiBD,EAAQC,gBAG1BC,EAAS,IAAIN,KAA+C,IAAzCI,EAAQC,eAAuBE,SACN,iBAA3BH,EAAQC,eAEzBC,EAAS,IAAIN,KAAKI,EAAQC,gBAChBD,EAAQC,0BAA0BL,OAC5CM,EAASF,EAAQC,mBAEbC,GAAUE,MAAMF,EAAOG,aACrBV,EAAMO,CAAM,EAGdI,EAAmBN,IACxB,GAAkC,UAA9BA,EAAQO,kBAA+B,CAE1C,IAAIC,EAAiB,GAarB,OAZIC,MAAMC,QAAQV,EAAQW,UACzBH,EAAOR,EAAQW,SAEa,iBAArBX,EAAQW,UACM,OAArBX,EAAQW,UACR,WAAYX,EAAQW,WAGpBH,EAAOC,MAAMG,KAAKZ,EAAQW,WAIP,IAAhBH,EAAKK,QAGFL,EAAKM,MAAMC,IAEjB,GAAIA,IAAQlB,EAAc,OAAO,EAIjC,MAAMmB,EAAUD,EACdE,QAAQ,gBAAiB,SACzBA,QAAQ,MAAO,OAGjB,OADc,IAAIC,OAAW,IAAAF,MAChBG,KAAKtB,EAAa,GAEhC,CACD,OAAO,CAAI,EAGNuB,EAAmB5B,EAAY6B,QAAQrB,UAC5C,MAAMsB,aAAW5B,aAAA,EAAAA,EAAc6B,qCAAiBvB,EAAQwB,OAAO,EACzDC,EAAazB,EAAQ0B,WAAY,EACjCC,EAAe5E,EAAiCiD,GAChD4B,EAAa7B,EAAaC,GAC1B6B,EAAgBvB,EAAgBN,GAStC,OANEsB,IACAG,GACDE,GACAC,GACAC,CAEgB,IAIZC,EAAgBV,EAAiBC,QACrCU,GAA8B,UAAxBA,EAAExB,oBAEJyB,EAAgBZ,EAAiBC,QACrCU,GAA8B,UAAxBA,EAAExB,oBAGV5B,EAAYyC,GACZvC,EAAuBiD,GAEvBG,YAAW,KACVlD,EAAuBiD,EAAc,GACnCE,EAAAA,iBAAiBC,kBACpB,CAAC,MAAOC,GACRjD,EAAS,+BACT,CAAS,QACTF,GAAa,EACb,CAtHA,CAuHF,KAAG,CACF/B,EACAP,EACAC,EACAG,EACAC,IAGKqF,EAA2BhD,eACzBiD,GAAqBhD,EAAAA,eAAA,OAAA,OAAA,GAAA,YAC3BL,GAAa,GACbE,EAAS,MACT,IACC,MAAOD,EAAOc,SAAiBnD,EAAiByF,GAChD,GAAIpD,IAAUc,EAEb,YADAb,EAAS,iCAIwB,UAA9Ba,EAAQO,kBACX1B,EAAuB,CAACmB,IAExBjB,EAAuB,CAACiB,IAEzBrB,EAAY,CAACqB,GACb,CAAC,MAAOoC,GACRjD,EAAS,oDACT,CAAS,QACTF,GAAa,EACb,CACF,KACA,CAACpC,IAGI0F,EAA8BlD,eAC5BiD,GAAqBhD,EAAAA,eAAA,OAAA,OAAA,GAAA,YAC3B,IAAKpC,EACJ,OAGD,MAAOgC,SAAepC,EAA4BI,EAASoF,GAY1D3D,GAAa6D,GAASA,EAAKnB,QAAQU,GAAMA,EAAEP,KAAOc,MAClDzD,GAAwB2D,GACvBA,EAAKnB,QAAQU,GAAMA,EAAEP,KAAOc,MAE7BvD,GAAwByD,GACvBA,EAAKnB,QAAQU,GAAMA,EAAEP,KAAOc,KAG9B,KACD,CAACpF,EAASJ,IAyBX,OAtBAQ,EAAAA,WAAU,WACT,MAAMmF,EACL,QAD0BC,EAAAzF,aAAM,EAANA,EAAQ0F,aAClC,IAAAD,OAAA,EAAAA,EAAAE,eAAaC,YAIVJ,GAAsBxF,EACzBoF,EAAyBI,GAK1BrD,GAAe,GACb,CACFnC,aAAA,EAAAA,EAAQ6C,SACQ,QAAhB4C,EAAAzF,aAAA,EAAAA,EAAQ0F,aAAQ,IAAAD,OAAA,EAAAA,EAAAE,EAAAA,aAAaC,YAC7B3F,EACAkC,EACAiD,EACApF,IAGM,CACNyB,WACAE,sBACAE,sBACAE,YACAE,QACA4D,oBAAqBP,EACrBQ,QAAS3D,EACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function e(r){var a,
|
|
1
|
+
"use strict";function e(e){return e.replace(/<br\s*\/?>\s*(<br\s*\/?>)\s*(<\/p>)/gi,"$1$2")}function r(e){var n,a,s;if(e.nodeType===Node.TEXT_NODE)return null!==(n=e.textContent)&&void 0!==n?n:"";if(e.nodeType!==Node.ELEMENT_NODE)return"";const t=e,c=t.tagName.toLowerCase(),i=function(e){return Array.from(e.childNodes).map(r).join("")}(t);switch(c){case"strong":case"b":return function(e,r){return e?e.includes("\n")?e.split("\n").map((e=>e?`${r}${e}${r}`:"")).join("\n"):`${r}${e}${r}`:""}(i,"*");case"em":case"i":case"s":case"del":case"strike":case"code":case"pre":case"a":case"ul":case"ol":case"body":default:return i;case"br":return"\n";case"p":case"div":return 1===t.childNodes.length&&"br"===(null===(s=null===(a=t.firstChild)||void 0===a?void 0:a.tagName)||void 0===s?void 0:s.toLowerCase())?"\n":i?`${i}\n`:"\n";case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return i?`*${i}*\n`:"\n";case"li":return`• ${i.trimEnd()}\n`;case"blockquote":return i+"\n"}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.toLiveChatText=function(n){if(!n)return"";if("undefined"!=typeof document){return r((new DOMParser).parseFromString(e(n),"text/html").body).trim()}return function(r){return e(r).replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi,"*$1*").replace(/<b[^>]*>([\s\S]*?)<\/b>/gi,"*$1*").replace(/<em[^>]*>([\s\S]*?)<\/em>/gi,"$1").replace(/<i[^>]*>([\s\S]*?)<\/i>/gi,"$1").replace(/<s[^>]*>([\s\S]*?)<\/s>/gi,"$1").replace(/<del[^>]*>([\s\S]*?)<\/del>/gi,"$1").replace(/<a[^>]*>([\s\S]*?)<\/a>/gi,"$1").replace(/<p[^>]*>\s*<br\s*\/?>\s*<\/p>/gi,"\n").replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n").replace(/<\/div>/gi,"\n").replace(/<\/h[1-6]>/gi,"\n").replace(/<li[^>]*>/gi,"• ").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/ /g," ").trim()}(n)};
|
|
2
2
|
//# sourceMappingURL=toLiveChatText.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toLiveChatText.js","sources":["../../../../src/editor/serializers/toLiveChatText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to LiveChat plain text.\n *\n * LiveChat supports **bold** (`*text*`) but NOT italic or strikethrough.\n * Italic and strikethrough marks are stripped — the inner text is preserved.\n * Links emit only the display text (href is dropped).\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const lcText = toLiveChatText(html);\n * sendLiveChat({ text: lcText });\n */\nexport function toLiveChatText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(html
|
|
1
|
+
{"version":3,"file":"toLiveChatText.js","sources":["../../../../src/editor/serializers/toLiveChatText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to LiveChat plain text.\n *\n * LiveChat supports **bold** (`*text*`) but NOT italic or strikethrough.\n * Italic and strikethrough marks are stripped — the inner text is preserved.\n * Links emit only the display text (href is dropped).\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const lcText = toLiveChatText(html);\n * sendLiveChat({ text: lcText });\n */\nexport function toLiveChatText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(\n\t\t\tundoPortableBrs(html),\n\t\t\t'text/html',\n\t\t);\n\t\treturn serializeNode(doc.body).trim();\n\t}\n\n\treturn ssrFallback(html);\n}\n\n/**\n * toPortableHtml doubles a trailing <br> inside content paragraphs so email\n * clients render the blank line. Messaging channels don't need this — undo\n * the doubling so each <br> maps to exactly one \\n.\n */\nfunction undoPortableBrs(html: string): string {\n\treturn html.replace(/<br\\s*\\/?>\\s*(<br\\s*\\/?>)\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * Wrap each non-empty line individually with a marker so formatting works\n * across line breaks (LiveChat *bold* is single-line only, like WhatsApp).\n */\nfunction wrapPerLine(inner: string, marker: string): string {\n\tif (!inner) return '';\n\tif (!inner.includes('\\n')) return `${marker}${inner}${marker}`;\n\treturn inner\n\t\t.split('\\n')\n\t\t.map((line) => (line ? `${marker}${line}${marker}` : ''))\n\t\t.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Browser path — DOM-based, handles nested marks correctly\n// ---------------------------------------------------------------------------\n\nfunction serializeNode(node: Node): string {\n\tif (node.nodeType === Node.TEXT_NODE) return node.textContent ?? '';\n\tif (node.nodeType !== Node.ELEMENT_NODE) return '';\n\n\tconst el = node as Element;\n\tconst tag = el.tagName.toLowerCase();\n\tconst inner = serializeChildren(el);\n\n\tswitch (tag) {\n\t\tcase 'strong':\n\t\tcase 'b':\n\t\t\treturn wrapPerLine(inner, '*');\n\n\t\t// Italic and strike: strip marks, keep inner text\n\t\tcase 'em':\n\t\tcase 'i':\n\t\tcase 's':\n\t\tcase 'del':\n\t\tcase 'strike':\n\t\t\treturn inner;\n\n\t\tcase 'code':\n\t\tcase 'pre':\n\t\t\treturn inner;\n\n\t\tcase 'a':\n\t\t\t// LiveChat does not support hyperlinks — emit display text only\n\t\t\treturn inner;\n\n\t\tcase 'br':\n\t\t\treturn '\\n';\n\n\t\tcase 'p':\n\t\tcase 'div': {\n\t\t\tconst isBlankLine =\n\t\t\t\tel.childNodes.length === 1 &&\n\t\t\t\t(el.firstChild as Element)?.tagName?.toLowerCase() === 'br';\n\t\t\treturn isBlankLine ? '\\n' : inner ? `${inner}\\n` : '\\n';\n\t\t}\n\n\t\tcase 'h1':\n\t\tcase 'h2':\n\t\tcase 'h3':\n\t\tcase 'h4':\n\t\tcase 'h5':\n\t\tcase 'h6':\n\t\t\treturn inner ? `*${inner}*\\n` : '\\n';\n\n\t\tcase 'ul':\n\t\tcase 'ol':\n\t\t\treturn inner;\n\n\t\tcase 'li':\n\t\t\treturn `• ${inner.trimEnd()}\\n`;\n\n\t\tcase 'blockquote':\n\t\t\treturn inner + '\\n';\n\n\t\tcase 'body':\n\t\tdefault:\n\t\t\treturn inner;\n\t}\n}\n\nfunction serializeChildren(el: Element): string {\n\treturn Array.from(el.childNodes).map(serializeNode).join('');\n}\n\n// ---------------------------------------------------------------------------\n// SSR / Node.js fallback — regex-based, best-effort for simple markup\n// ---------------------------------------------------------------------------\n\nfunction ssrFallback(html: string): string {\n\treturn undoPortableBrs(html)\n\t\t.replace(/<strong[^>]*>([\\s\\S]*?)<\\/strong>/gi, '*$1*')\n\t\t.replace(/<b[^>]*>([\\s\\S]*?)<\\/b>/gi, '*$1*')\n\t\t.replace(/<em[^>]*>([\\s\\S]*?)<\\/em>/gi, '$1')\n\t\t.replace(/<i[^>]*>([\\s\\S]*?)<\\/i>/gi, '$1')\n\t\t.replace(/<s[^>]*>([\\s\\S]*?)<\\/s>/gi, '$1')\n\t\t.replace(/<del[^>]*>([\\s\\S]*?)<\\/del>/gi, '$1')\n\t\t.replace(/<a[^>]*>([\\s\\S]*?)<\\/a>/gi, '$1')\n\t\t.replace(/<p[^>]*>\\s*<br\\s*\\/?>\\s*<\\/p>/gi, '\\n')\n\t\t.replace(/<br\\s*\\/?>/gi, '\\n')\n\t\t.replace(/<\\/p>/gi, '\\n')\n\t\t.replace(/<\\/div>/gi, '\\n')\n\t\t.replace(/<\\/h[1-6]>/gi, '\\n')\n\t\t.replace(/<li[^>]*>/gi, '• ')\n\t\t.replace(/<\\/li>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/ /g, ' ')\n\t\t.trim();\n}\n"],"names":["undoPortableBrs","html","replace","serializeNode","node","nodeType","Node","TEXT_NODE","textContent","_a","ELEMENT_NODE","el","tag","tagName","toLowerCase","inner","Array","from","childNodes","map","join","serializeChildren","marker","includes","split","line","wrapPerLine","length","_c","_b","firstChild","trimEnd","document","DOMParser","parseFromString","body","trim","ssrFallback"],"mappings":"aA+BA,SAASA,EAAgBC,GACxB,OAAOA,EAAKC,QAAQ,wCAAyC,OAC9D,CAmBA,SAASC,EAAcC,aACtB,GAAIA,EAAKC,WAAaC,KAAKC,UAAW,OAAuB,UAAhBH,EAAKI,mBAAW,IAAAC,EAAAA,EAAI,GACjE,GAAIL,EAAKC,WAAaC,KAAKI,aAAc,MAAO,GAEhD,MAAMC,EAAKP,EACLQ,EAAMD,EAAGE,QAAQC,cACjBC,EA0DP,SAA2BJ,GAC1B,OAAOK,MAAMC,KAAKN,EAAGO,YAAYC,IAAIhB,GAAeiB,KAAK,GAC1D,CA5DeC,CAAkBV,GAEhC,OAAQC,GACP,IAAK,SACL,IAAK,IACJ,OAxBH,SAAqBG,EAAeO,GACnC,OAAKP,EACAA,EAAMQ,SAAS,MACbR,EACLS,MAAM,MACNL,KAAKM,GAAUA,KAAUH,IAASG,IAAOH,IAAW,KACpDF,KAAK,SAJ8BE,IAASP,IAAQO,IADnC,EAMpB,CAiBUI,CAAYX,EAAO,KAG3B,IAAK,KACL,IAAK,IACL,IAAK,IACL,IAAK,MACL,IAAK,SAGL,IAAK,OACL,IAAK,MAGL,IAAK,IAuBL,IAAK,KACL,IAAK,KASL,IAAK,OACL,QACC,OAAOA,EA/BR,IAAK,KACJ,MAAO,KAER,IAAK,IACL,IAAK,MAIJ,OAF0B,IAAzBJ,EAAGO,WAAWS,QACyC,QAApB,QAAnCC,EAA0B,QAA1BC,EAAClB,EAAGmB,kBAAsB,IAAAD,OAAA,EAAAA,EAAEhB,eAAO,IAAAe,OAAA,EAAAA,EAAEd,eACjB,KAAOC,KAAWA,MAAY,KAGpD,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACJ,OAAOA,EAAQ,IAAIA,OAAa,KAMjC,IAAK,KACJ,WAAYA,EAAMgB,cAEnB,IAAK,aACJ,OAAOhB,EAAQ,KAMlB,+EAtGM,SAAyBd,GAC9B,IAAKA,EAAM,MAAO,GAElB,GAAwB,oBAAb+B,SAA0B,CAKpC,OAAO7B,GAJK,IAAI8B,WAAYC,gBAC3BlC,EAAgBC,GAChB,aAEwBkC,MAAMC,MAC/B,CAED,OAqGD,SAAqBnC,GACpB,OAAOD,EAAgBC,GACrBC,QAAQ,sCAAuC,QAC/CA,QAAQ,4BAA6B,QACrCA,QAAQ,8BAA+B,MACvCA,QAAQ,4BAA6B,MACrCA,QAAQ,4BAA6B,MACrCA,QAAQ,gCAAiC,MACzCA,QAAQ,4BAA6B,MACrCA,QAAQ,kCAAmC,MAC3CA,QAAQ,eAAgB,MACxBA,QAAQ,UAAW,MACnBA,QAAQ,YAAa,MACrBA,QAAQ,eAAgB,MACxBA,QAAQ,cAAe,MACvBA,QAAQ,WAAY,MACpBA,QAAQ,WAAY,IACpBA,QAAQ,SAAU,KAClBA,QAAQ,QAAS,KACjBA,QAAQ,QAAS,KACjBA,QAAQ,UAAW,KACnBkC,MACH,CA3HQC,CAAYpC,EACpB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function e(r){var
|
|
1
|
+
"use strict";function e(e){return e.replace(/<br\s*\/?>\s*(<br\s*\/?>)\s*(<\/p>)/gi,"$1$2")}function r(e,r){return e?e.includes("\n")?e.split("\n").map((e=>e?`${r}${e}${r}`:"")).join("\n"):`${r}${e}${r}`:""}function n(e){var s,t,a;if(e.nodeType===Node.TEXT_NODE)return null!==(s=e.textContent)&&void 0!==s?s:"";if(e.nodeType!==Node.ELEMENT_NODE)return"";const c=e,i=c.tagName.toLowerCase(),l=function(e){return Array.from(e.childNodes).map(n).join("")}(c);switch(i){case"strong":case"b":return r(l,"*");case"em":case"i":return r(l,"_");case"s":case"del":case"strike":return r(l,"~");case"code":return l?`\`${l}\``:"";case"pre":return l?`\`\`\`${l}\`\`\``:"";case"a":case"ul":case"ol":case"body":default:return l;case"br":return"\n";case"p":case"div":return 1===c.childNodes.length&&"br"===(null===(a=null===(t=c.firstChild)||void 0===t?void 0:t.tagName)||void 0===a?void 0:a.toLowerCase())?"\n":l?`${l}\n`:"\n";case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return l?`*${l}*\n`:"\n";case"li":return`• ${l.trimEnd()}\n`;case"blockquote":return l.split("\n").map((e=>e?`> ${e}`:"")).join("\n")+"\n"}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.toWhatsAppText=function(r){if(!r)return"";if("undefined"!=typeof document){return n((new DOMParser).parseFromString(e(r),"text/html").body).trim()}return function(r){return e(r).replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi,"*$1*").replace(/<b[^>]*>([\s\S]*?)<\/b>/gi,"*$1*").replace(/<em[^>]*>([\s\S]*?)<\/em>/gi,"_$1_").replace(/<i[^>]*>([\s\S]*?)<\/i>/gi,"_$1_").replace(/<s[^>]*>([\s\S]*?)<\/s>/gi,"~$1~").replace(/<del[^>]*>([\s\S]*?)<\/del>/gi,"~$1~").replace(/<code[^>]*>([\s\S]*?)<\/code>/gi,"`$1`").replace(/<a[^>]*>([\s\S]*?)<\/a>/gi,"$1").replace(/<p[^>]*>\s*<br\s*\/?>\s*<\/p>/gi,"\n").replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n").replace(/<\/div>/gi,"\n").replace(/<\/h[1-6]>/gi,"\n").replace(/<li[^>]*>/gi,"• ").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/ /g," ").trim()}(r)};
|
|
2
2
|
//# sourceMappingURL=toWhatsAppText.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toWhatsAppText.js","sources":["../../../../src/editor/serializers/toWhatsAppText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to WhatsApp-flavoured markdown text.\n *\n * WhatsApp formatting:\n * *bold* _italic_ ~strikethrough~ ```monospace```\n *\n * Links: WhatsApp does not support hyperlinks.\n * <a href=\"https://…\">Visit us</a> → Visit us (display text kept, href dropped)\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const waText = toWhatsAppText(html);\n * sendWhatsApp({ text: waText });\n */\nexport function toWhatsAppText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(html
|
|
1
|
+
{"version":3,"file":"toWhatsAppText.js","sources":["../../../../src/editor/serializers/toWhatsAppText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to WhatsApp-flavoured markdown text.\n *\n * WhatsApp formatting:\n * *bold* _italic_ ~strikethrough~ ```monospace```\n *\n * Links: WhatsApp does not support hyperlinks.\n * <a href=\"https://…\">Visit us</a> → Visit us (display text kept, href dropped)\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const waText = toWhatsAppText(html);\n * sendWhatsApp({ text: waText });\n */\nexport function toWhatsAppText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(\n\t\t\tundoPortableBrs(html),\n\t\t\t'text/html',\n\t\t);\n\t\treturn serializeNode(doc.body).trim();\n\t}\n\n\t// SSR / Node.js fallback — handles simple non-nested markup\n\treturn ssrFallback(html);\n}\n\n/**\n * toPortableHtml doubles a trailing <br> inside content paragraphs so email\n * clients render the blank line. Messaging channels don't need this — undo\n * the doubling so each <br> maps to exactly one \\n.\n */\nfunction undoPortableBrs(html: string): string {\n\treturn html.replace(/<br\\s*\\/?>\\s*(<br\\s*\\/?>)\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * WhatsApp requires formatting markers (*bold*, _italic_, ~strike~) to be\n * contained within a single line. If the inner text of a mark element spans\n * multiple lines (e.g. <strong>line1<br>line2</strong>), wrap each non-empty\n * line individually.\n */\nfunction wrapPerLine(inner: string, marker: string): string {\n\tif (!inner) return '';\n\tif (!inner.includes('\\n')) return `${marker}${inner}${marker}`;\n\treturn inner\n\t\t.split('\\n')\n\t\t.map((line) => (line ? `${marker}${line}${marker}` : ''))\n\t\t.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Browser path — DOM-based, handles nested marks correctly\n// ---------------------------------------------------------------------------\n\nfunction serializeNode(node: Node): string {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent ?? '';\n\t}\n\tif (node.nodeType !== Node.ELEMENT_NODE) return '';\n\n\tconst el = node as Element;\n\tconst tag = el.tagName.toLowerCase();\n\tconst inner = serializeChildren(el);\n\n\tswitch (tag) {\n\t\tcase 'strong':\n\t\tcase 'b':\n\t\t\treturn wrapPerLine(inner, '*');\n\n\t\tcase 'em':\n\t\tcase 'i':\n\t\t\treturn wrapPerLine(inner, '_');\n\n\t\tcase 's':\n\t\tcase 'del':\n\t\tcase 'strike':\n\t\t\treturn wrapPerLine(inner, '~');\n\n\t\tcase 'code':\n\t\t\treturn inner ? `\\`${inner}\\`` : '';\n\n\t\tcase 'pre':\n\t\t\treturn inner ? `\\`\\`\\`${inner}\\`\\`\\`` : '';\n\n\t\tcase 'a':\n\t\t\treturn inner;\n\n\t\tcase 'br':\n\t\t\treturn '\\n';\n\n\t\tcase 'p':\n\t\tcase 'div': {\n\t\t\tconst isBlankLine =\n\t\t\t\tel.childNodes.length === 1 &&\n\t\t\t\t(el.firstChild as Element)?.tagName?.toLowerCase() === 'br';\n\t\t\treturn isBlankLine ? '\\n' : inner ? `${inner}\\n` : '\\n';\n\t\t}\n\n\t\tcase 'h1':\n\t\tcase 'h2':\n\t\tcase 'h3':\n\t\tcase 'h4':\n\t\tcase 'h5':\n\t\tcase 'h6':\n\t\t\treturn inner ? `*${inner}*\\n` : '\\n';\n\n\t\tcase 'ul':\n\t\tcase 'ol':\n\t\t\treturn inner;\n\n\t\tcase 'li':\n\t\t\treturn `• ${inner.trimEnd()}\\n`;\n\n\t\tcase 'blockquote':\n\t\t\treturn (\n\t\t\t\tinner\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.map((l) => (l ? `> ${l}` : ''))\n\t\t\t\t\t.join('\\n') + '\\n'\n\t\t\t);\n\n\t\tcase 'body':\n\t\tdefault:\n\t\t\treturn inner;\n\t}\n}\n\nfunction serializeChildren(el: Element): string {\n\treturn Array.from(el.childNodes).map(serializeNode).join('');\n}\n\n// ---------------------------------------------------------------------------\n// SSR fallback — regex-based, best-effort for simple markup\n// ---------------------------------------------------------------------------\n\nfunction ssrFallback(html: string): string {\n\treturn undoPortableBrs(html)\n\t\t.replace(/<strong[^>]*>([\\s\\S]*?)<\\/strong>/gi, '*$1*')\n\t\t.replace(/<b[^>]*>([\\s\\S]*?)<\\/b>/gi, '*$1*')\n\t\t.replace(/<em[^>]*>([\\s\\S]*?)<\\/em>/gi, '_$1_')\n\t\t.replace(/<i[^>]*>([\\s\\S]*?)<\\/i>/gi, '_$1_')\n\t\t.replace(/<s[^>]*>([\\s\\S]*?)<\\/s>/gi, '~$1~')\n\t\t.replace(/<del[^>]*>([\\s\\S]*?)<\\/del>/gi, '~$1~')\n\t\t.replace(/<code[^>]*>([\\s\\S]*?)<\\/code>/gi, '`$1`')\n\t\t.replace(/<a[^>]*>([\\s\\S]*?)<\\/a>/gi, '$1')\n\t\t.replace(/<p[^>]*>\\s*<br\\s*\\/?>\\s*<\\/p>/gi, '\\n')\n\t\t.replace(/<br\\s*\\/?>/gi, '\\n')\n\t\t.replace(/<\\/p>/gi, '\\n')\n\t\t.replace(/<\\/div>/gi, '\\n')\n\t\t.replace(/<\\/h[1-6]>/gi, '\\n')\n\t\t.replace(/<li[^>]*>/gi, '• ')\n\t\t.replace(/<\\/li>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/ /g, ' ')\n\t\t.trim();\n}\n"],"names":["undoPortableBrs","html","replace","wrapPerLine","inner","marker","includes","split","map","line","join","serializeNode","node","nodeType","Node","TEXT_NODE","textContent","_a","ELEMENT_NODE","el","tag","tagName","toLowerCase","Array","from","childNodes","serializeChildren","length","_c","_b","firstChild","trimEnd","l","document","DOMParser","parseFromString","body","trim","ssrFallback"],"mappings":"aAkCA,SAASA,EAAgBC,GACxB,OAAOA,EAAKC,QAAQ,wCAAyC,OAC9D,CAQA,SAASC,EAAYC,EAAeC,GACnC,OAAKD,EACAA,EAAME,SAAS,MACbF,EACLG,MAAM,MACNC,KAAKC,GAAUA,KAAUJ,IAASI,IAAOJ,IAAW,KACpDK,KAAK,SAJ8BL,IAASD,IAAQC,IADnC,EAMpB,CAMA,SAASM,EAAcC,aACtB,GAAIA,EAAKC,WAAaC,KAAKC,UAC1B,OAAuB,UAAhBH,EAAKI,mBAAW,IAAAC,EAAAA,EAAI,GAE5B,GAAIL,EAAKC,WAAaC,KAAKI,aAAc,MAAO,GAEhD,MAAMC,EAAKP,EACLQ,EAAMD,EAAGE,QAAQC,cACjBlB,EAiEP,SAA2Be,GAC1B,OAAOI,MAAMC,KAAKL,EAAGM,YAAYjB,IAAIG,GAAeD,KAAK,GAC1D,CAnEegB,CAAkBP,GAEhC,OAAQC,GACP,IAAK,SACL,IAAK,IACJ,OAAOjB,EAAYC,EAAO,KAE3B,IAAK,KACL,IAAK,IACJ,OAAOD,EAAYC,EAAO,KAE3B,IAAK,IACL,IAAK,MACL,IAAK,SACJ,OAAOD,EAAYC,EAAO,KAE3B,IAAK,OACJ,OAAOA,EAAQ,KAAKA,MAAY,GAEjC,IAAK,MACJ,OAAOA,EAAQ,SAASA,UAAgB,GAEzC,IAAK,IAsBL,IAAK,KACL,IAAK,KAcL,IAAK,OACL,QACC,OAAOA,EApCR,IAAK,KACJ,MAAO,KAER,IAAK,IACL,IAAK,MAIJ,OAF0B,IAAzBe,EAAGM,WAAWE,QACyC,QAApB,QAAnCC,EAA0B,QAA1BC,EAACV,EAAGW,kBAAsB,IAAAD,OAAA,EAAAA,EAAER,eAAO,IAAAO,OAAA,EAAAA,EAAEN,eACjB,KAAOlB,KAAWA,MAAY,KAGpD,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACJ,OAAOA,EAAQ,IAAIA,OAAa,KAMjC,IAAK,KACJ,WAAYA,EAAM2B,cAEnB,IAAK,aACJ,OACC3B,EACEG,MAAM,MACNC,KAAKwB,GAAOA,EAAI,KAAKA,IAAM,KAC3BtB,KAAK,MAAQ,KAOnB,+EAlHM,SAAyBT,GAC9B,IAAKA,EAAM,MAAO,GAElB,GAAwB,oBAAbgC,SAA0B,CAKpC,OAAOtB,GAJK,IAAIuB,WAAYC,gBAC3BnC,EAAgBC,GAChB,aAEwBmC,MAAMC,MAC/B,CAGD,OAgHD,SAAqBpC,GACpB,OAAOD,EAAgBC,GACrBC,QAAQ,sCAAuC,QAC/CA,QAAQ,4BAA6B,QACrCA,QAAQ,8BAA+B,QACvCA,QAAQ,4BAA6B,QACrCA,QAAQ,4BAA6B,QACrCA,QAAQ,gCAAiC,QACzCA,QAAQ,kCAAmC,QAC3CA,QAAQ,4BAA6B,MACrCA,QAAQ,kCAAmC,MAC3CA,QAAQ,eAAgB,MACxBA,QAAQ,UAAW,MACnBA,QAAQ,YAAa,MACrBA,QAAQ,eAAgB,MACxBA,QAAQ,cAAe,MACvBA,QAAQ,WAAY,MACpBA,QAAQ,WAAY,IACpBA,QAAQ,SAAU,KAClBA,QAAQ,QAAS,KACjBA,QAAQ,QAAS,KACjBA,QAAQ,UAAW,KACnBmC,MACH,CAvIQC,CAAYrC,EACpB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__awaiter as e}from"../../../_virtual/_tslib.js";import{useState as t,useCallback as r,useEffect as o}from"react";import{ANIMATION_TIMING as i}from"../constants/animations.js";import{QUERY_PARAMS as a,LOCAL_STORAGE_KEYS as n}from"../constants/localStorageKeys.js";const l=l=>{let{fetchVisibleFeatures:d,getStoreFeatureProgress:u,fetchFeatureById:s,markFeatureAsViewedForStore:c,isFeatureApplicableToCurrentPage:f,module:p,router:g}=l;var v;const y=(()=>{const[e,r]=t(null);return o((()=>{if("undefined"!=typeof window){let e=null;try{if(e=localStorage.getItem(n.STORE_ID)||localStorage.getItem(n.BIK_STORE_ID)||localStorage.getItem(n.CURRENT_STORE)||localStorage.getItem(n.STORE),!e){const t=localStorage.getItem(n.CURRENT_USER);t&&(e=JSON.parse(t).storeId)}e||(e=new URLSearchParams(window.location.search).get(n.QUERY_PARAM))}catch(e){}r(e)}}),[]),e})(),[m,D]=t([]),[U,
|
|
1
|
+
import{__awaiter as e}from"../../../_virtual/_tslib.js";import{useState as t,useCallback as r,useEffect as o}from"react";import{ANIMATION_TIMING as i}from"../constants/animations.js";import{QUERY_PARAMS as a,LOCAL_STORAGE_KEYS as n}from"../constants/localStorageKeys.js";const l=l=>{let{fetchVisibleFeatures:d,getStoreFeatureProgress:u,fetchFeatureById:s,markFeatureAsViewedForStore:c,isFeatureApplicableToCurrentPage:f,module:p,router:g}=l;var v;const y=(()=>{const[e,r]=t(null);return o((()=>{if("undefined"!=typeof window){let e=null;try{if(e=localStorage.getItem(n.STORE_ID)||localStorage.getItem(n.BIK_STORE_ID)||localStorage.getItem(n.CURRENT_STORE)||localStorage.getItem(n.STORE),!e){const t=localStorage.getItem(n.CURRENT_USER);t&&(e=JSON.parse(t).storeId)}e||(e=new URLSearchParams(window.location.search).get(n.QUERY_PARAM))}catch(e){}r(e)}}),[]),e})(),[m,D]=t([]),[U,h]=t([]),[w,R]=t([]),[I,S]=t(!1),[T,x]=t(null),E=r((()=>e(void 0,void 0,void 0,(function*(){if(y){S(!0),x(null);try{const[e,t]=yield d(p);if(e||!t)return void x("Failed to fetch features");const[r,o]=yield u(y),a=new Date,n="undefined"!=typeof window?window.location.pathname:"",l=e=>{if(!e.expirationDate)return!0;let t;return"object"==typeof e.expirationDate&&null!==e.expirationDate&&("seconds"in e.expirationDate||"nanoseconds"in e.expirationDate)?t=new Date(1e3*e.expirationDate.seconds):"string"==typeof e.expirationDate?t=new Date(e.expirationDate):e.expirationDate instanceof Date&&(t=e.expirationDate),!(!t||isNaN(t.getTime()))&&a<t},s=e=>{if("Major"===e.featureUpdateType){let t=[];return Array.isArray(e.pageUrls)?t=e.pageUrls:"object"==typeof e.pageUrls&&null!==e.pageUrls&&"length"in e.pageUrls&&(t=Array.from(e.pageUrls)),0===t.length||t.some((e=>{if(e===n)return!0;const t=e.replace(/\[([^\]]+)\]/g,"[^/]+").replace(/\//g,"\\/");return new RegExp(`^${t}$`).test(n)}))}return!0},c=t.filter((e=>{var t;const r=(null===(t=null==o?void 0:o.viewedFeatures)||void 0===t?void 0:t[e.id])||!1,i=e.archived||!1,a=f(e),n=l(e),d=s(e);return!r&&!i&&a&&n&&d})),g=c.filter((e=>"Major"===e.featureUpdateType)),v=c.filter((e=>"Minor"===e.featureUpdateType));D(c),h(g),setTimeout((()=>{R(v)}),i.MINOR_POPUP_DELAY)}catch(e){x("An unexpected error occurred")}finally{S(!1)}}}))),[y,d,u,f,p]),F=r((t=>e(void 0,void 0,void 0,(function*(){S(!0),x(null);try{const[e,r]=yield s(t);if(e||!r)return void x("Failed to fetch feature by ID");"Major"===r.featureUpdateType?h([r]):R([r]),D([r])}catch(e){x("An unexpected error occurred while fetching by ID")}finally{S(!1)}}))),[s]),A=r((t=>e(void 0,void 0,void 0,(function*(){if(!y)return;const[e]=yield c(y,t);D((e=>e.filter((e=>e.id!==t)))),h((e=>e.filter((e=>e.id!==t)))),R((e=>e.filter((e=>e.id!==t))))}))),[y,c]);return o((()=>{var e;const t=null===(e=null==g?void 0:g.query)||void 0===e?void 0:e[a.FEATURE_ID];t&&g?F(t):E()}),[null==g?void 0:g.pathname,null===(v=null==g?void 0:g.query)||void 0===v?void 0:v[a.FEATURE_ID],y,E,F,g]),{features:m,majorUpdateFeatures:U,minorUpdateFeatures:w,isLoading:I,error:T,markFeatureAsViewed:A,refetch:E}};export{l as useFeatureAnnouncements};
|
|
2
2
|
//# sourceMappingURL=useFeatureAnnouncements.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFeatureAnnouncements.js","sources":["../../../../../src/components/feature-announcements/hooks/useFeatureAnnouncements.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport {\n\tANIMATION_TIMING,\n\tLOCAL_STORAGE_KEYS,\n\tQUERY_PARAMS,\n} from '../constants';\nimport {\n\tFeatureAnnouncement,\n\tFeatureAnnouncementRepository,\n\tRouterProps,\n} from '../types';\n\nconst useStoreId = () => {\n\tconst [storeId, setStoreId] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (typeof window !== 'undefined') {\n\t\t\tlet storedStoreId: string | null = null;\n\n\t\t\ttry {\n\t\t\t\tstoredStoreId =\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.BIK_STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.CURRENT_STORE) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE);\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tconst currentUser = localStorage.getItem(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.CURRENT_USER,\n\t\t\t\t\t);\n\t\t\t\t\tif (currentUser) {\n\t\t\t\t\t\tconst parsed = JSON.parse(currentUser);\n\t\t\t\t\t\tstoredStoreId = parsed.storeId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tstoredStoreId = new URLSearchParams(window.location.search).get(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.QUERY_PARAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t// Failed to get store ID\n\t\t\t}\n\t\t\tsetStoreId(storedStoreId);\n\t\t}\n\t}, []);\n\n\treturn storeId;\n};\n\nexport const useFeatureAnnouncements = ({\n\tfetchVisibleFeatures,\n\tgetStoreFeatureProgress,\n\tfetchFeatureById,\n\tmarkFeatureAsViewedForStore,\n\tisFeatureApplicableToCurrentPage,\n\tmodule,\n\trouter,\n}: FeatureAnnouncementRepository & {\n\tmodule?: string;\n\trouter?: RouterProps;\n}) => {\n\tconst storeId = useStoreId();\n\tconst [features, setFeatures] = useState<FeatureAnnouncement[]>([]);\n\tconst [majorUpdateFeatures, setMajorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [minorUpdateFeatures, setMinorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst fetchFeatures = useCallback(async () => {\n\t\tif (!storeId) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsLoading(true);\n\t\tsetError(null);\n\n\t\ttry {\n\t\t\tconst [featuresError, allFeatures] = await fetchVisibleFeatures(module);\n\n\t\t\tif (featuresError || !allFeatures) {\n\t\t\t\tsetError('Failed to fetch features');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [progressError, userProgress] = await getStoreFeatureProgress(\n\t\t\t\tstoreId,\n\t\t\t);\n\n\t\t\tif (progressError) {\n\t\t\t\t// Progress fetch failed, treating all features as new\n\t\t\t}\n\n\t\t\tconst now = new Date();\n\t\t\tconst currentRoute =\n\t\t\t\ttypeof window !== 'undefined' ? window.location.pathname : '';\n\n\t\t\tconst isNotExpired = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (!feature.expirationDate) return true;\n\t\t\t\tlet expiry: Date | undefined;\n\t\t\t\t// Firestore timestamp object\n\t\t\t\tif (\n\t\t\t\t\ttypeof feature.expirationDate === 'object' &&\n\t\t\t\t\tfeature.expirationDate !== null &&\n\t\t\t\t\t('seconds' in feature.expirationDate ||\n\t\t\t\t\t\t'nanoseconds' in feature.expirationDate)\n\t\t\t\t) {\n\t\t\t\t\t// Firestore timestamp: { seconds: number, nanoseconds?: number }\n\t\t\t\t\texpiry = new Date((feature.expirationDate as any).seconds * 1000);\n\t\t\t\t} else if (typeof feature.expirationDate === 'string') {\n\t\t\t\t\t// String date\n\t\t\t\t\texpiry = new Date(feature.expirationDate);\n\t\t\t\t} else if (feature.expirationDate instanceof Date) {\n\t\t\t\t\texpiry = feature.expirationDate;\n\t\t\t\t}\n\t\t\t\tif (!expiry || isNaN(expiry.getTime())) return false;\n\t\t\t\treturn now < expiry;\n\t\t\t};\n\n\t\t\tconst isOnCorrectPage = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\t// Accept both array and object (Firestore array)\n\t\t\t\t\tlet urls: string[] = [];\n\t\t\t\t\tif (Array.isArray(feature.pageUrls)) {\n\t\t\t\t\t\turls = feature.pageUrls;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\ttypeof feature.pageUrls === 'object' &&\n\t\t\t\t\t\tfeature.pageUrls !== null &&\n\t\t\t\t\t\t'length' in feature.pageUrls\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Firestore array\n\t\t\t\t\t\turls = Array.from(feature.pageUrls as any);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for exact match or dynamic route pattern match\n\t\t\t\t\treturn urls.some((url) => {\n\t\t\t\t\t\t// Exact match\n\t\t\t\t\t\tif (url === currentRoute) return true;\n\n\t\t\t\t\t\t// Dynamic route matching (e.g., /campaign/details/[broadcastId])\n\t\t\t\t\t\t// Convert Next.js route pattern to regex\n\t\t\t\t\t\tconst pattern = url\n\t\t\t\t\t\t\t.replace(/\\[([^\\]]+)\\]/g, '[^/]+') // Replace [param] with [^/]+\n\t\t\t\t\t\t\t.replace(/\\//g, '\\\\/'); // Escape forward slashes\n\n\t\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`);\n\t\t\t\t\t\treturn regex.test(currentRoute);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst unviewedFeatures = allFeatures.filter((feature) => {\n\t\t\t\tconst isViewed = userProgress?.viewedFeatures?.[feature.id] || false;\n\t\t\t\tconst isArchived = feature.archived || false;\n\t\t\t\tconst isApplicable = isFeatureApplicableToCurrentPage(feature);\n\t\t\t\tconst notExpired = isNotExpired(feature);\n\t\t\t\tconst onCorrectPage = isOnCorrectPage(feature);\n\n\t\t\t\tconst shouldShow =\n\t\t\t\t\t!isViewed &&\n\t\t\t\t\t!isArchived &&\n\t\t\t\t\tisApplicable &&\n\t\t\t\t\tnotExpired &&\n\t\t\t\t\tonCorrectPage;\n\n\t\t\t\treturn shouldShow;\n\t\t\t});\n\n\t\t\t// Separate major and minor updates\n\t\t\tconst majorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Major',\n\t\t\t);\n\t\t\tconst minorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Minor',\n\t\t\t);\n\n\t\t\tsetFeatures(unviewedFeatures);\n\t\t\tsetMajorUpdateFeatures(majorFeatures);\n\t\t\t// Delay minor popups by 2 seconds\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetMinorUpdateFeatures(minorFeatures);\n\t\t\t}, ANIMATION_TIMING.MINOR_POPUP_DELAY);\n\t\t} catch (err) {\n\t\t\tsetError('An unexpected error occurred');\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t}, [\n\t\tstoreId,\n\t\tfetchVisibleFeatures,\n\t\tgetStoreFeatureProgress,\n\t\tisFeatureApplicableToCurrentPage,\n\t\tmodule,\n\t]);\n\n\tconst fetchFeatureByIdCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst [error, feature] = await fetchFeatureById(featureId);\n\t\t\t\tif (error || !feature) {\n\t\t\t\t\tsetError('Failed to fetch feature by ID');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\tsetMajorUpdateFeatures([feature]);\n\t\t\t\t} else {\n\t\t\t\t\tsetMinorUpdateFeatures([feature]);\n\t\t\t\t}\n\t\t\t\tsetFeatures([feature]);\n\t\t\t} catch (err) {\n\t\t\t\tsetError('An unexpected error occurred while fetching by ID');\n\t\t\t} finally {\n\t\t\t\tsetIsLoading(false);\n\t\t\t}\n\t\t},\n\t\t[fetchFeatureById],\n\t);\n\n\tconst markFeatureAsViewedCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tif (!storeId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [error] = await markFeatureAsViewedForStore(storeId, featureId);\n\n\t\t\tif (!error) {\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Failed to mark feature as viewed, removing from local state anyway\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\t[storeId, markFeatureAsViewedForStore],\n\t);\n\n\tuseEffect(() => {\n\t\tconst featureIdFromQuery = router?.query?.[\n\t\t\tQUERY_PARAMS.FEATURE_ID\n\t\t] as string;\n\n\t\t// In test mode with featureId, ONLY fetch that specific feature\n\t\tif (featureIdFromQuery && router) {\n\t\t\tfetchFeatureByIdCallback(featureIdFromQuery);\n\t\t\treturn; // Don't run fetchFeatures\n\t\t}\n\n\t\t// Normal mode: fetch all features\n\t\tfetchFeatures();\n\t}, [\n\t\trouter?.pathname,\n\t\trouter?.query?.[QUERY_PARAMS.FEATURE_ID],\n\t\tstoreId,\n\t\tfetchFeatures,\n\t\tfetchFeatureByIdCallback,\n\t\trouter,\n\t]);\n\n\treturn {\n\t\tfeatures,\n\t\tmajorUpdateFeatures,\n\t\tminorUpdateFeatures,\n\t\tisLoading,\n\t\terror,\n\t\tmarkFeatureAsViewed: markFeatureAsViewedCallback,\n\t\trefetch: fetchFeatures,\n\t};\n};\n"],"names":["useFeatureAnnouncements","_ref","fetchVisibleFeatures","getStoreFeatureProgress","fetchFeatureById","markFeatureAsViewedForStore","isFeatureApplicableToCurrentPage","module","router","storeId","useStoreId","setStoreId","useState","useEffect","window","storedStoreId","localStorage","getItem","LOCAL_STORAGE_KEYS","STORE_ID","BIK_STORE_ID","CURRENT_STORE","STORE","currentUser","CURRENT_USER","JSON","parse","URLSearchParams","location","search","get","QUERY_PARAM","e","features","setFeatures","majorUpdateFeatures","setMajorUpdateFeatures","minorUpdateFeatures","setMinorUpdateFeatures","isLoading","setIsLoading","error","setError","fetchFeatures","useCallback","__awaiter","featuresError","allFeatures","progressError","userProgress","now","Date","currentRoute","pathname","isNotExpired","feature","expirationDate","expiry","seconds","isNaN","getTime","isOnCorrectPage","featureUpdateType","urls","Array","isArray","pageUrls","from","some","url","pattern","replace","RegExp","test","unviewedFeatures","filter","isViewed","viewedFeatures","id","isArchived","archived","isApplicable","notExpired","onCorrectPage","majorFeatures","f","minorFeatures","setTimeout","ANIMATION_TIMING","MINOR_POPUP_DELAY","err","fetchFeatureByIdCallback","featureId","markFeatureAsViewedCallback","prev","featureIdFromQuery","_a","query","QUERY_PARAMS","FEATURE_ID","markFeatureAsViewed","refetch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useFeatureAnnouncements.js","sources":["../../../../../src/components/feature-announcements/hooks/useFeatureAnnouncements.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport {\n\tANIMATION_TIMING,\n\tLOCAL_STORAGE_KEYS,\n\tQUERY_PARAMS,\n} from '../constants';\nimport {\n\tFeatureAnnouncement,\n\tFeatureAnnouncementRepository,\n\tRouterProps,\n} from '../types';\n\nconst useStoreId = () => {\n\tconst [storeId, setStoreId] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (typeof window !== 'undefined') {\n\t\t\tlet storedStoreId: string | null = null;\n\n\t\t\ttry {\n\t\t\t\tstoredStoreId =\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.BIK_STORE_ID) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.CURRENT_STORE) ||\n\t\t\t\t\tlocalStorage.getItem(LOCAL_STORAGE_KEYS.STORE);\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tconst currentUser = localStorage.getItem(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.CURRENT_USER,\n\t\t\t\t\t);\n\t\t\t\t\tif (currentUser) {\n\t\t\t\t\t\tconst parsed = JSON.parse(currentUser);\n\t\t\t\t\t\tstoredStoreId = parsed.storeId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!storedStoreId) {\n\t\t\t\t\tstoredStoreId = new URLSearchParams(window.location.search).get(\n\t\t\t\t\t\tLOCAL_STORAGE_KEYS.QUERY_PARAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\t// Failed to get store ID\n\t\t\t}\n\t\t\tsetStoreId(storedStoreId);\n\t\t}\n\t}, []);\n\n\treturn storeId;\n};\n\nexport const useFeatureAnnouncements = ({\n\tfetchVisibleFeatures,\n\tgetStoreFeatureProgress,\n\tfetchFeatureById,\n\tmarkFeatureAsViewedForStore,\n\tisFeatureApplicableToCurrentPage,\n\tmodule,\n\trouter,\n}: FeatureAnnouncementRepository & {\n\tmodule?: string;\n\trouter?: RouterProps;\n}) => {\n\tconst storeId = useStoreId();\n\tconst [features, setFeatures] = useState<FeatureAnnouncement[]>([]);\n\tconst [majorUpdateFeatures, setMajorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [minorUpdateFeatures, setMinorUpdateFeatures] = useState<\n\t\tFeatureAnnouncement[]\n\t>([]);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst fetchFeatures = useCallback(async () => {\n\t\tif (!storeId) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsLoading(true);\n\t\tsetError(null);\n\n\t\ttry {\n\t\t\tconst [featuresError, allFeatures] = await fetchVisibleFeatures(module);\n\n\t\t\tif (featuresError || !allFeatures) {\n\t\t\t\tsetError('Failed to fetch features');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [progressError, userProgress] = await getStoreFeatureProgress(\n\t\t\t\tstoreId,\n\t\t\t);\n\n\t\t\tif (progressError) {\n\t\t\t\t// Progress fetch failed, treating all features as new\n\t\t\t}\n\n\t\t\tconst now = new Date();\n\t\t\tconst currentRoute =\n\t\t\t\ttypeof window !== 'undefined' ? window.location.pathname : '';\n\n\t\t\tconst isNotExpired = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (!feature.expirationDate) return true;\n\t\t\t\tlet expiry: Date | undefined;\n\t\t\t\t// Firestore timestamp object\n\t\t\t\tif (\n\t\t\t\t\ttypeof feature.expirationDate === 'object' &&\n\t\t\t\t\tfeature.expirationDate !== null &&\n\t\t\t\t\t('seconds' in feature.expirationDate ||\n\t\t\t\t\t\t'nanoseconds' in feature.expirationDate)\n\t\t\t\t) {\n\t\t\t\t\t// Firestore timestamp: { seconds: number, nanoseconds?: number }\n\t\t\t\t\texpiry = new Date((feature.expirationDate as any).seconds * 1000);\n\t\t\t\t} else if (typeof feature.expirationDate === 'string') {\n\t\t\t\t\t// String date\n\t\t\t\t\texpiry = new Date(feature.expirationDate);\n\t\t\t\t} else if (feature.expirationDate instanceof Date) {\n\t\t\t\t\texpiry = feature.expirationDate;\n\t\t\t\t}\n\t\t\t\tif (!expiry || isNaN(expiry.getTime())) return false;\n\t\t\t\treturn now < expiry;\n\t\t\t};\n\n\t\t\tconst isOnCorrectPage = (feature: FeatureAnnouncement) => {\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\t// Accept both array and object (Firestore array)\n\t\t\t\t\tlet urls: string[] = [];\n\t\t\t\t\tif (Array.isArray(feature.pageUrls)) {\n\t\t\t\t\t\turls = feature.pageUrls;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\ttypeof feature.pageUrls === 'object' &&\n\t\t\t\t\t\tfeature.pageUrls !== null &&\n\t\t\t\t\t\t'length' in feature.pageUrls\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Firestore array\n\t\t\t\t\t\turls = Array.from(feature.pageUrls as any);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Empty pageUrls means no restriction — show on all pages\n\t\t\t\t\tif (urls.length === 0) return true;\n\n\t\t\t\t\t// Check for exact match or dynamic route pattern match\n\t\t\t\t\treturn urls.some((url) => {\n\t\t\t\t\t\t// Exact match\n\t\t\t\t\t\tif (url === currentRoute) return true;\n\n\t\t\t\t\t\t// Dynamic route matching (e.g., /campaign/details/[broadcastId])\n\t\t\t\t\t\t// Convert Next.js route pattern to regex\n\t\t\t\t\t\tconst pattern = url\n\t\t\t\t\t\t\t.replace(/\\[([^\\]]+)\\]/g, '[^/]+') // Replace [param] with [^/]+\n\t\t\t\t\t\t\t.replace(/\\//g, '\\\\/'); // Escape forward slashes\n\n\t\t\t\t\t\tconst regex = new RegExp(`^${pattern}$`);\n\t\t\t\t\t\treturn regex.test(currentRoute);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst unviewedFeatures = allFeatures.filter((feature) => {\n\t\t\t\tconst isViewed = userProgress?.viewedFeatures?.[feature.id] || false;\n\t\t\t\tconst isArchived = feature.archived || false;\n\t\t\t\tconst isApplicable = isFeatureApplicableToCurrentPage(feature);\n\t\t\t\tconst notExpired = isNotExpired(feature);\n\t\t\t\tconst onCorrectPage = isOnCorrectPage(feature);\n\n\t\t\t\tconst shouldShow =\n\t\t\t\t\t!isViewed &&\n\t\t\t\t\t!isArchived &&\n\t\t\t\t\tisApplicable &&\n\t\t\t\t\tnotExpired &&\n\t\t\t\t\tonCorrectPage;\n\n\t\t\t\treturn shouldShow;\n\t\t\t});\n\n\t\t\t// Separate major and minor updates\n\t\t\tconst majorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Major',\n\t\t\t);\n\t\t\tconst minorFeatures = unviewedFeatures.filter(\n\t\t\t\t(f) => f.featureUpdateType === 'Minor',\n\t\t\t);\n\n\t\t\tsetFeatures(unviewedFeatures);\n\t\t\tsetMajorUpdateFeatures(majorFeatures);\n\t\t\t// Delay minor popups by 2 seconds\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetMinorUpdateFeatures(minorFeatures);\n\t\t\t}, ANIMATION_TIMING.MINOR_POPUP_DELAY);\n\t\t} catch (err) {\n\t\t\tsetError('An unexpected error occurred');\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t}, [\n\t\tstoreId,\n\t\tfetchVisibleFeatures,\n\t\tgetStoreFeatureProgress,\n\t\tisFeatureApplicableToCurrentPage,\n\t\tmodule,\n\t]);\n\n\tconst fetchFeatureByIdCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst [error, feature] = await fetchFeatureById(featureId);\n\t\t\t\tif (error || !feature) {\n\t\t\t\t\tsetError('Failed to fetch feature by ID');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (feature.featureUpdateType === 'Major') {\n\t\t\t\t\tsetMajorUpdateFeatures([feature]);\n\t\t\t\t} else {\n\t\t\t\t\tsetMinorUpdateFeatures([feature]);\n\t\t\t\t}\n\t\t\t\tsetFeatures([feature]);\n\t\t\t} catch (err) {\n\t\t\t\tsetError('An unexpected error occurred while fetching by ID');\n\t\t\t} finally {\n\t\t\t\tsetIsLoading(false);\n\t\t\t}\n\t\t},\n\t\t[fetchFeatureById],\n\t);\n\n\tconst markFeatureAsViewedCallback = useCallback(\n\t\tasync (featureId: string) => {\n\t\t\tif (!storeId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [error] = await markFeatureAsViewedForStore(storeId, featureId);\n\n\t\t\tif (!error) {\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Failed to mark feature as viewed, removing from local state anyway\n\t\t\t\tsetFeatures((prev) => prev.filter((f) => f.id !== featureId));\n\t\t\t\tsetMajorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t\tsetMinorUpdateFeatures((prev) =>\n\t\t\t\t\tprev.filter((f) => f.id !== featureId),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\t[storeId, markFeatureAsViewedForStore],\n\t);\n\n\tuseEffect(() => {\n\t\tconst featureIdFromQuery = router?.query?.[\n\t\t\tQUERY_PARAMS.FEATURE_ID\n\t\t] as string;\n\n\t\t// In test mode with featureId, ONLY fetch that specific feature\n\t\tif (featureIdFromQuery && router) {\n\t\t\tfetchFeatureByIdCallback(featureIdFromQuery);\n\t\t\treturn; // Don't run fetchFeatures\n\t\t}\n\n\t\t// Normal mode: fetch all features\n\t\tfetchFeatures();\n\t}, [\n\t\trouter?.pathname,\n\t\trouter?.query?.[QUERY_PARAMS.FEATURE_ID],\n\t\tstoreId,\n\t\tfetchFeatures,\n\t\tfetchFeatureByIdCallback,\n\t\trouter,\n\t]);\n\n\treturn {\n\t\tfeatures,\n\t\tmajorUpdateFeatures,\n\t\tminorUpdateFeatures,\n\t\tisLoading,\n\t\terror,\n\t\tmarkFeatureAsViewed: markFeatureAsViewedCallback,\n\t\trefetch: fetchFeatures,\n\t};\n};\n"],"names":["useFeatureAnnouncements","_ref","fetchVisibleFeatures","getStoreFeatureProgress","fetchFeatureById","markFeatureAsViewedForStore","isFeatureApplicableToCurrentPage","module","router","storeId","useStoreId","setStoreId","useState","useEffect","window","storedStoreId","localStorage","getItem","LOCAL_STORAGE_KEYS","STORE_ID","BIK_STORE_ID","CURRENT_STORE","STORE","currentUser","CURRENT_USER","JSON","parse","URLSearchParams","location","search","get","QUERY_PARAM","e","features","setFeatures","majorUpdateFeatures","setMajorUpdateFeatures","minorUpdateFeatures","setMinorUpdateFeatures","isLoading","setIsLoading","error","setError","fetchFeatures","useCallback","__awaiter","featuresError","allFeatures","progressError","userProgress","now","Date","currentRoute","pathname","isNotExpired","feature","expirationDate","expiry","seconds","isNaN","getTime","isOnCorrectPage","featureUpdateType","urls","Array","isArray","pageUrls","from","length","some","url","pattern","replace","RegExp","test","unviewedFeatures","filter","isViewed","viewedFeatures","id","isArchived","archived","isApplicable","notExpired","onCorrectPage","majorFeatures","f","minorFeatures","setTimeout","ANIMATION_TIMING","MINOR_POPUP_DELAY","err","fetchFeatureByIdCallback","featureId","markFeatureAsViewedCallback","prev","featureIdFromQuery","_a","query","QUERY_PARAMS","FEATURE_ID","markFeatureAsViewed","refetch"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){if(!e)return"";if("undefined"!=typeof document){return
|
|
1
|
+
function e(e){if(!e)return"";if("undefined"!=typeof document){return n((new DOMParser).parseFromString(r(e),"text/html").body).trim()}return function(e){return r(e).replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi,"*$1*").replace(/<b[^>]*>([\s\S]*?)<\/b>/gi,"*$1*").replace(/<em[^>]*>([\s\S]*?)<\/em>/gi,"$1").replace(/<i[^>]*>([\s\S]*?)<\/i>/gi,"$1").replace(/<s[^>]*>([\s\S]*?)<\/s>/gi,"$1").replace(/<del[^>]*>([\s\S]*?)<\/del>/gi,"$1").replace(/<a[^>]*>([\s\S]*?)<\/a>/gi,"$1").replace(/<p[^>]*>\s*<br\s*\/?>\s*<\/p>/gi,"\n").replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n").replace(/<\/div>/gi,"\n").replace(/<\/h[1-6]>/gi,"\n").replace(/<li[^>]*>/gi,"• ").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/ /g," ").trim()}(e)}function r(e){return e.replace(/<br\s*\/?>\s*(<br\s*\/?>)\s*(<\/p>)/gi,"$1$2")}function n(e){var r,a,c;if(e.nodeType===Node.TEXT_NODE)return null!==(r=e.textContent)&&void 0!==r?r:"";if(e.nodeType!==Node.ELEMENT_NODE)return"";const s=e,i=s.tagName.toLowerCase(),t=function(e){return Array.from(e.childNodes).map(n).join("")}(s);switch(i){case"strong":case"b":return function(e,r){return e?e.includes("\n")?e.split("\n").map((e=>e?`${r}${e}${r}`:"")).join("\n"):`${r}${e}${r}`:""}(t,"*");case"em":case"i":case"s":case"del":case"strike":case"code":case"pre":case"a":case"ul":case"ol":case"body":default:return t;case"br":return"\n";case"p":case"div":return 1===s.childNodes.length&&"br"===(null===(c=null===(a=s.firstChild)||void 0===a?void 0:a.tagName)||void 0===c?void 0:c.toLowerCase())?"\n":t?`${t}\n`:"\n";case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return t?`*${t}*\n`:"\n";case"li":return`• ${t.trimEnd()}\n`;case"blockquote":return t+"\n"}}export{e as toLiveChatText};
|
|
2
2
|
//# sourceMappingURL=toLiveChatText.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toLiveChatText.js","sources":["../../../../src/editor/serializers/toLiveChatText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to LiveChat plain text.\n *\n * LiveChat supports **bold** (`*text*`) but NOT italic or strikethrough.\n * Italic and strikethrough marks are stripped — the inner text is preserved.\n * Links emit only the display text (href is dropped).\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const lcText = toLiveChatText(html);\n * sendLiveChat({ text: lcText });\n */\nexport function toLiveChatText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(html
|
|
1
|
+
{"version":3,"file":"toLiveChatText.js","sources":["../../../../src/editor/serializers/toLiveChatText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to LiveChat plain text.\n *\n * LiveChat supports **bold** (`*text*`) but NOT italic or strikethrough.\n * Italic and strikethrough marks are stripped — the inner text is preserved.\n * Links emit only the display text (href is dropped).\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const lcText = toLiveChatText(html);\n * sendLiveChat({ text: lcText });\n */\nexport function toLiveChatText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(\n\t\t\tundoPortableBrs(html),\n\t\t\t'text/html',\n\t\t);\n\t\treturn serializeNode(doc.body).trim();\n\t}\n\n\treturn ssrFallback(html);\n}\n\n/**\n * toPortableHtml doubles a trailing <br> inside content paragraphs so email\n * clients render the blank line. Messaging channels don't need this — undo\n * the doubling so each <br> maps to exactly one \\n.\n */\nfunction undoPortableBrs(html: string): string {\n\treturn html.replace(/<br\\s*\\/?>\\s*(<br\\s*\\/?>)\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * Wrap each non-empty line individually with a marker so formatting works\n * across line breaks (LiveChat *bold* is single-line only, like WhatsApp).\n */\nfunction wrapPerLine(inner: string, marker: string): string {\n\tif (!inner) return '';\n\tif (!inner.includes('\\n')) return `${marker}${inner}${marker}`;\n\treturn inner\n\t\t.split('\\n')\n\t\t.map((line) => (line ? `${marker}${line}${marker}` : ''))\n\t\t.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Browser path — DOM-based, handles nested marks correctly\n// ---------------------------------------------------------------------------\n\nfunction serializeNode(node: Node): string {\n\tif (node.nodeType === Node.TEXT_NODE) return node.textContent ?? '';\n\tif (node.nodeType !== Node.ELEMENT_NODE) return '';\n\n\tconst el = node as Element;\n\tconst tag = el.tagName.toLowerCase();\n\tconst inner = serializeChildren(el);\n\n\tswitch (tag) {\n\t\tcase 'strong':\n\t\tcase 'b':\n\t\t\treturn wrapPerLine(inner, '*');\n\n\t\t// Italic and strike: strip marks, keep inner text\n\t\tcase 'em':\n\t\tcase 'i':\n\t\tcase 's':\n\t\tcase 'del':\n\t\tcase 'strike':\n\t\t\treturn inner;\n\n\t\tcase 'code':\n\t\tcase 'pre':\n\t\t\treturn inner;\n\n\t\tcase 'a':\n\t\t\t// LiveChat does not support hyperlinks — emit display text only\n\t\t\treturn inner;\n\n\t\tcase 'br':\n\t\t\treturn '\\n';\n\n\t\tcase 'p':\n\t\tcase 'div': {\n\t\t\tconst isBlankLine =\n\t\t\t\tel.childNodes.length === 1 &&\n\t\t\t\t(el.firstChild as Element)?.tagName?.toLowerCase() === 'br';\n\t\t\treturn isBlankLine ? '\\n' : inner ? `${inner}\\n` : '\\n';\n\t\t}\n\n\t\tcase 'h1':\n\t\tcase 'h2':\n\t\tcase 'h3':\n\t\tcase 'h4':\n\t\tcase 'h5':\n\t\tcase 'h6':\n\t\t\treturn inner ? `*${inner}*\\n` : '\\n';\n\n\t\tcase 'ul':\n\t\tcase 'ol':\n\t\t\treturn inner;\n\n\t\tcase 'li':\n\t\t\treturn `• ${inner.trimEnd()}\\n`;\n\n\t\tcase 'blockquote':\n\t\t\treturn inner + '\\n';\n\n\t\tcase 'body':\n\t\tdefault:\n\t\t\treturn inner;\n\t}\n}\n\nfunction serializeChildren(el: Element): string {\n\treturn Array.from(el.childNodes).map(serializeNode).join('');\n}\n\n// ---------------------------------------------------------------------------\n// SSR / Node.js fallback — regex-based, best-effort for simple markup\n// ---------------------------------------------------------------------------\n\nfunction ssrFallback(html: string): string {\n\treturn undoPortableBrs(html)\n\t\t.replace(/<strong[^>]*>([\\s\\S]*?)<\\/strong>/gi, '*$1*')\n\t\t.replace(/<b[^>]*>([\\s\\S]*?)<\\/b>/gi, '*$1*')\n\t\t.replace(/<em[^>]*>([\\s\\S]*?)<\\/em>/gi, '$1')\n\t\t.replace(/<i[^>]*>([\\s\\S]*?)<\\/i>/gi, '$1')\n\t\t.replace(/<s[^>]*>([\\s\\S]*?)<\\/s>/gi, '$1')\n\t\t.replace(/<del[^>]*>([\\s\\S]*?)<\\/del>/gi, '$1')\n\t\t.replace(/<a[^>]*>([\\s\\S]*?)<\\/a>/gi, '$1')\n\t\t.replace(/<p[^>]*>\\s*<br\\s*\\/?>\\s*<\\/p>/gi, '\\n')\n\t\t.replace(/<br\\s*\\/?>/gi, '\\n')\n\t\t.replace(/<\\/p>/gi, '\\n')\n\t\t.replace(/<\\/div>/gi, '\\n')\n\t\t.replace(/<\\/h[1-6]>/gi, '\\n')\n\t\t.replace(/<li[^>]*>/gi, '• ')\n\t\t.replace(/<\\/li>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/ /g, ' ')\n\t\t.trim();\n}\n"],"names":["toLiveChatText","html","document","serializeNode","DOMParser","parseFromString","undoPortableBrs","body","trim","replace","ssrFallback","node","nodeType","Node","TEXT_NODE","textContent","_a","ELEMENT_NODE","el","tag","tagName","toLowerCase","inner","Array","from","childNodes","map","join","serializeChildren","marker","includes","split","line","wrapPerLine","length","_c","_b","firstChild","trimEnd"],"mappings":"AAYM,SAAUA,EAAeC,GAC9B,IAAKA,EAAM,MAAO,GAElB,GAAwB,oBAAbC,SAA0B,CAKpC,OAAOC,GAJK,IAAIC,WAAYC,gBAC3BC,EAAgBL,GAChB,aAEwBM,MAAMC,MAC/B,CAED,OAqGD,SAAqBP,GACpB,OAAOK,EAAgBL,GACrBQ,QAAQ,sCAAuC,QAC/CA,QAAQ,4BAA6B,QACrCA,QAAQ,8BAA+B,MACvCA,QAAQ,4BAA6B,MACrCA,QAAQ,4BAA6B,MACrCA,QAAQ,gCAAiC,MACzCA,QAAQ,4BAA6B,MACrCA,QAAQ,kCAAmC,MAC3CA,QAAQ,eAAgB,MACxBA,QAAQ,UAAW,MACnBA,QAAQ,YAAa,MACrBA,QAAQ,eAAgB,MACxBA,QAAQ,cAAe,MACvBA,QAAQ,WAAY,MACpBA,QAAQ,WAAY,IACpBA,QAAQ,SAAU,KAClBA,QAAQ,QAAS,KACjBA,QAAQ,QAAS,KACjBA,QAAQ,UAAW,KACnBD,MACH,CA3HQE,CAAYT,EACpB,CAOA,SAASK,EAAgBL,GACxB,OAAOA,EAAKQ,QAAQ,wCAAyC,OAC9D,CAmBA,SAASN,EAAcQ,aACtB,GAAIA,EAAKC,WAAaC,KAAKC,UAAW,OAAuB,UAAhBH,EAAKI,mBAAW,IAAAC,EAAAA,EAAI,GACjE,GAAIL,EAAKC,WAAaC,KAAKI,aAAc,MAAO,GAEhD,MAAMC,EAAKP,EACLQ,EAAMD,EAAGE,QAAQC,cACjBC,EA0DP,SAA2BJ,GAC1B,OAAOK,MAAMC,KAAKN,EAAGO,YAAYC,IAAIvB,GAAewB,KAAK,GAC1D,CA5DeC,CAAkBV,GAEhC,OAAQC,GACP,IAAK,SACL,IAAK,IACJ,OAxBH,SAAqBG,EAAeO,GACnC,OAAKP,EACAA,EAAMQ,SAAS,MACbR,EACLS,MAAM,MACNL,KAAKM,GAAUA,KAAUH,IAASG,IAAOH,IAAW,KACpDF,KAAK,SAJ8BE,IAASP,IAAQO,IADnC,EAMpB,CAiBUI,CAAYX,EAAO,KAG3B,IAAK,KACL,IAAK,IACL,IAAK,IACL,IAAK,MACL,IAAK,SAGL,IAAK,OACL,IAAK,MAGL,IAAK,IAuBL,IAAK,KACL,IAAK,KASL,IAAK,OACL,QACC,OAAOA,EA/BR,IAAK,KACJ,MAAO,KAER,IAAK,IACL,IAAK,MAIJ,OAF0B,IAAzBJ,EAAGO,WAAWS,QACyC,QAApB,QAAnCC,EAA0B,QAA1BC,EAAClB,EAAGmB,kBAAsB,IAAAD,OAAA,EAAAA,EAAEhB,eAAO,IAAAe,OAAA,EAAAA,EAAEd,eACjB,KAAOC,KAAWA,MAAY,KAGpD,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACJ,OAAOA,EAAQ,IAAIA,OAAa,KAMjC,IAAK,KACJ,WAAYA,EAAMgB,cAEnB,IAAK,aACJ,OAAOhB,EAAQ,KAMlB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){if(!e)return"";if("undefined"!=typeof document){return
|
|
1
|
+
function e(e){if(!e)return"";if("undefined"!=typeof document){return a((new DOMParser).parseFromString(r(e),"text/html").body).trim()}return function(e){return r(e).replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi,"*$1*").replace(/<b[^>]*>([\s\S]*?)<\/b>/gi,"*$1*").replace(/<em[^>]*>([\s\S]*?)<\/em>/gi,"_$1_").replace(/<i[^>]*>([\s\S]*?)<\/i>/gi,"_$1_").replace(/<s[^>]*>([\s\S]*?)<\/s>/gi,"~$1~").replace(/<del[^>]*>([\s\S]*?)<\/del>/gi,"~$1~").replace(/<code[^>]*>([\s\S]*?)<\/code>/gi,"`$1`").replace(/<a[^>]*>([\s\S]*?)<\/a>/gi,"$1").replace(/<p[^>]*>\s*<br\s*\/?>\s*<\/p>/gi,"\n").replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>/gi,"\n").replace(/<\/div>/gi,"\n").replace(/<\/h[1-6]>/gi,"\n").replace(/<li[^>]*>/gi,"• ").replace(/<\/li>/gi,"\n").replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/ /g," ").trim()}(e)}function r(e){return e.replace(/<br\s*\/?>\s*(<br\s*\/?>)\s*(<\/p>)/gi,"$1$2")}function n(e,r){return e?e.includes("\n")?e.split("\n").map((e=>e?`${r}${e}${r}`:"")).join("\n"):`${r}${e}${r}`:""}function a(e){var r,c,s;if(e.nodeType===Node.TEXT_NODE)return null!==(r=e.textContent)&&void 0!==r?r:"";if(e.nodeType!==Node.ELEMENT_NODE)return"";const t=e,i=t.tagName.toLowerCase(),l=function(e){return Array.from(e.childNodes).map(a).join("")}(t);switch(i){case"strong":case"b":return n(l,"*");case"em":case"i":return n(l,"_");case"s":case"del":case"strike":return n(l,"~");case"code":return l?`\`${l}\``:"";case"pre":return l?`\`\`\`${l}\`\`\``:"";case"a":case"ul":case"ol":case"body":default:return l;case"br":return"\n";case"p":case"div":return 1===t.childNodes.length&&"br"===(null===(s=null===(c=t.firstChild)||void 0===c?void 0:c.tagName)||void 0===s?void 0:s.toLowerCase())?"\n":l?`${l}\n`:"\n";case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return l?`*${l}*\n`:"\n";case"li":return`• ${l.trimEnd()}\n`;case"blockquote":return l.split("\n").map((e=>e?`> ${e}`:"")).join("\n")+"\n"}}export{e as toWhatsAppText};
|
|
2
2
|
//# sourceMappingURL=toWhatsAppText.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toWhatsAppText.js","sources":["../../../../src/editor/serializers/toWhatsAppText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to WhatsApp-flavoured markdown text.\n *\n * WhatsApp formatting:\n * *bold* _italic_ ~strikethrough~ ```monospace```\n *\n * Links: WhatsApp does not support hyperlinks.\n * <a href=\"https://…\">Visit us</a> → Visit us (display text kept, href dropped)\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const waText = toWhatsAppText(html);\n * sendWhatsApp({ text: waText });\n */\nexport function toWhatsAppText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(html
|
|
1
|
+
{"version":3,"file":"toWhatsAppText.js","sources":["../../../../src/editor/serializers/toWhatsAppText.ts"],"sourcesContent":["/**\n * Converts TipTap HTML to WhatsApp-flavoured markdown text.\n *\n * WhatsApp formatting:\n * *bold* _italic_ ~strikethrough~ ```monospace```\n *\n * Links: WhatsApp does not support hyperlinks.\n * <a href=\"https://…\">Visit us</a> → Visit us (display text kept, href dropped)\n *\n * @example\n * const { html } = ref.current?.getContent();\n * const waText = toWhatsAppText(html);\n * sendWhatsApp({ text: waText });\n */\nexport function toWhatsAppText(html: string): string {\n\tif (!html) return '';\n\n\tif (typeof document !== 'undefined') {\n\t\tconst doc = new DOMParser().parseFromString(\n\t\t\tundoPortableBrs(html),\n\t\t\t'text/html',\n\t\t);\n\t\treturn serializeNode(doc.body).trim();\n\t}\n\n\t// SSR / Node.js fallback — handles simple non-nested markup\n\treturn ssrFallback(html);\n}\n\n/**\n * toPortableHtml doubles a trailing <br> inside content paragraphs so email\n * clients render the blank line. Messaging channels don't need this — undo\n * the doubling so each <br> maps to exactly one \\n.\n */\nfunction undoPortableBrs(html: string): string {\n\treturn html.replace(/<br\\s*\\/?>\\s*(<br\\s*\\/?>)\\s*(<\\/p>)/gi, '$1$2');\n}\n\n/**\n * WhatsApp requires formatting markers (*bold*, _italic_, ~strike~) to be\n * contained within a single line. If the inner text of a mark element spans\n * multiple lines (e.g. <strong>line1<br>line2</strong>), wrap each non-empty\n * line individually.\n */\nfunction wrapPerLine(inner: string, marker: string): string {\n\tif (!inner) return '';\n\tif (!inner.includes('\\n')) return `${marker}${inner}${marker}`;\n\treturn inner\n\t\t.split('\\n')\n\t\t.map((line) => (line ? `${marker}${line}${marker}` : ''))\n\t\t.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Browser path — DOM-based, handles nested marks correctly\n// ---------------------------------------------------------------------------\n\nfunction serializeNode(node: Node): string {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent ?? '';\n\t}\n\tif (node.nodeType !== Node.ELEMENT_NODE) return '';\n\n\tconst el = node as Element;\n\tconst tag = el.tagName.toLowerCase();\n\tconst inner = serializeChildren(el);\n\n\tswitch (tag) {\n\t\tcase 'strong':\n\t\tcase 'b':\n\t\t\treturn wrapPerLine(inner, '*');\n\n\t\tcase 'em':\n\t\tcase 'i':\n\t\t\treturn wrapPerLine(inner, '_');\n\n\t\tcase 's':\n\t\tcase 'del':\n\t\tcase 'strike':\n\t\t\treturn wrapPerLine(inner, '~');\n\n\t\tcase 'code':\n\t\t\treturn inner ? `\\`${inner}\\`` : '';\n\n\t\tcase 'pre':\n\t\t\treturn inner ? `\\`\\`\\`${inner}\\`\\`\\`` : '';\n\n\t\tcase 'a':\n\t\t\treturn inner;\n\n\t\tcase 'br':\n\t\t\treturn '\\n';\n\n\t\tcase 'p':\n\t\tcase 'div': {\n\t\t\tconst isBlankLine =\n\t\t\t\tel.childNodes.length === 1 &&\n\t\t\t\t(el.firstChild as Element)?.tagName?.toLowerCase() === 'br';\n\t\t\treturn isBlankLine ? '\\n' : inner ? `${inner}\\n` : '\\n';\n\t\t}\n\n\t\tcase 'h1':\n\t\tcase 'h2':\n\t\tcase 'h3':\n\t\tcase 'h4':\n\t\tcase 'h5':\n\t\tcase 'h6':\n\t\t\treturn inner ? `*${inner}*\\n` : '\\n';\n\n\t\tcase 'ul':\n\t\tcase 'ol':\n\t\t\treturn inner;\n\n\t\tcase 'li':\n\t\t\treturn `• ${inner.trimEnd()}\\n`;\n\n\t\tcase 'blockquote':\n\t\t\treturn (\n\t\t\t\tinner\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.map((l) => (l ? `> ${l}` : ''))\n\t\t\t\t\t.join('\\n') + '\\n'\n\t\t\t);\n\n\t\tcase 'body':\n\t\tdefault:\n\t\t\treturn inner;\n\t}\n}\n\nfunction serializeChildren(el: Element): string {\n\treturn Array.from(el.childNodes).map(serializeNode).join('');\n}\n\n// ---------------------------------------------------------------------------\n// SSR fallback — regex-based, best-effort for simple markup\n// ---------------------------------------------------------------------------\n\nfunction ssrFallback(html: string): string {\n\treturn undoPortableBrs(html)\n\t\t.replace(/<strong[^>]*>([\\s\\S]*?)<\\/strong>/gi, '*$1*')\n\t\t.replace(/<b[^>]*>([\\s\\S]*?)<\\/b>/gi, '*$1*')\n\t\t.replace(/<em[^>]*>([\\s\\S]*?)<\\/em>/gi, '_$1_')\n\t\t.replace(/<i[^>]*>([\\s\\S]*?)<\\/i>/gi, '_$1_')\n\t\t.replace(/<s[^>]*>([\\s\\S]*?)<\\/s>/gi, '~$1~')\n\t\t.replace(/<del[^>]*>([\\s\\S]*?)<\\/del>/gi, '~$1~')\n\t\t.replace(/<code[^>]*>([\\s\\S]*?)<\\/code>/gi, '`$1`')\n\t\t.replace(/<a[^>]*>([\\s\\S]*?)<\\/a>/gi, '$1')\n\t\t.replace(/<p[^>]*>\\s*<br\\s*\\/?>\\s*<\\/p>/gi, '\\n')\n\t\t.replace(/<br\\s*\\/?>/gi, '\\n')\n\t\t.replace(/<\\/p>/gi, '\\n')\n\t\t.replace(/<\\/div>/gi, '\\n')\n\t\t.replace(/<\\/h[1-6]>/gi, '\\n')\n\t\t.replace(/<li[^>]*>/gi, '• ')\n\t\t.replace(/<\\/li>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/ /g, ' ')\n\t\t.trim();\n}\n"],"names":["toWhatsAppText","html","document","serializeNode","DOMParser","parseFromString","undoPortableBrs","body","trim","replace","ssrFallback","wrapPerLine","inner","marker","includes","split","map","line","join","node","nodeType","Node","TEXT_NODE","textContent","_a","ELEMENT_NODE","el","tag","tagName","toLowerCase","Array","from","childNodes","serializeChildren","length","_c","_b","firstChild","trimEnd","l"],"mappings":"AAcM,SAAUA,EAAeC,GAC9B,IAAKA,EAAM,MAAO,GAElB,GAAwB,oBAAbC,SAA0B,CAKpC,OAAOC,GAJK,IAAIC,WAAYC,gBAC3BC,EAAgBL,GAChB,aAEwBM,MAAMC,MAC/B,CAGD,OAgHD,SAAqBP,GACpB,OAAOK,EAAgBL,GACrBQ,QAAQ,sCAAuC,QAC/CA,QAAQ,4BAA6B,QACrCA,QAAQ,8BAA+B,QACvCA,QAAQ,4BAA6B,QACrCA,QAAQ,4BAA6B,QACrCA,QAAQ,gCAAiC,QACzCA,QAAQ,kCAAmC,QAC3CA,QAAQ,4BAA6B,MACrCA,QAAQ,kCAAmC,MAC3CA,QAAQ,eAAgB,MACxBA,QAAQ,UAAW,MACnBA,QAAQ,YAAa,MACrBA,QAAQ,eAAgB,MACxBA,QAAQ,cAAe,MACvBA,QAAQ,WAAY,MACpBA,QAAQ,WAAY,IACpBA,QAAQ,SAAU,KAClBA,QAAQ,QAAS,KACjBA,QAAQ,QAAS,KACjBA,QAAQ,UAAW,KACnBD,MACH,CAvIQE,CAAYT,EACpB,CAOA,SAASK,EAAgBL,GACxB,OAAOA,EAAKQ,QAAQ,wCAAyC,OAC9D,CAQA,SAASE,EAAYC,EAAeC,GACnC,OAAKD,EACAA,EAAME,SAAS,MACbF,EACLG,MAAM,MACNC,KAAKC,GAAUA,KAAUJ,IAASI,IAAOJ,IAAW,KACpDK,KAAK,SAJ8BL,IAASD,IAAQC,IADnC,EAMpB,CAMA,SAASV,EAAcgB,aACtB,GAAIA,EAAKC,WAAaC,KAAKC,UAC1B,OAAuB,UAAhBH,EAAKI,mBAAW,IAAAC,EAAAA,EAAI,GAE5B,GAAIL,EAAKC,WAAaC,KAAKI,aAAc,MAAO,GAEhD,MAAMC,EAAKP,EACLQ,EAAMD,EAAGE,QAAQC,cACjBjB,EAiEP,SAA2Bc,GAC1B,OAAOI,MAAMC,KAAKL,EAAGM,YAAYhB,IAAIb,GAAee,KAAK,GAC1D,CAnEee,CAAkBP,GAEhC,OAAQC,GACP,IAAK,SACL,IAAK,IACJ,OAAOhB,EAAYC,EAAO,KAE3B,IAAK,KACL,IAAK,IACJ,OAAOD,EAAYC,EAAO,KAE3B,IAAK,IACL,IAAK,MACL,IAAK,SACJ,OAAOD,EAAYC,EAAO,KAE3B,IAAK,OACJ,OAAOA,EAAQ,KAAKA,MAAY,GAEjC,IAAK,MACJ,OAAOA,EAAQ,SAASA,UAAgB,GAEzC,IAAK,IAsBL,IAAK,KACL,IAAK,KAcL,IAAK,OACL,QACC,OAAOA,EApCR,IAAK,KACJ,MAAO,KAER,IAAK,IACL,IAAK,MAIJ,OAF0B,IAAzBc,EAAGM,WAAWE,QACyC,QAApB,QAAnCC,EAA0B,QAA1BC,EAACV,EAAGW,kBAAsB,IAAAD,OAAA,EAAAA,EAAER,eAAO,IAAAO,OAAA,EAAAA,EAAEN,eACjB,KAAOjB,KAAWA,MAAY,KAGpD,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACL,IAAK,KACJ,OAAOA,EAAQ,IAAIA,OAAa,KAMjC,IAAK,KACJ,WAAYA,EAAM0B,cAEnB,IAAK,aACJ,OACC1B,EACEG,MAAM,MACNC,KAAKuB,GAAOA,EAAI,KAAKA,IAAM,KAC3BrB,KAAK,MAAQ,KAOnB"}
|