@pugpigbolt/bridge 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/bootstrap-B7Gwb_jl.mjs +1 -0
- package/dist/chunks/development-CzUmtUQR.mjs +1 -0
- package/dist/{index-BjDhZ_q5.d.mts → chunks/index-BWVJLr0k.d.mts} +3 -107
- package/dist/chunks/mockBridgeService-M_48ewQL.mjs +27 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -19
- package/dist/vue.d.mts +8 -45
- package/dist/vue.mjs +1 -213
- package/package.json +1 -1
- package/dist/bootstrap-DQyoNFue.mjs +0 -530
- package/dist/development-Bl8-5E3g.mjs +0 -56
- package/dist/mockBridgeService-vnaAIjCu.mjs +0 -272
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t){let{paywall_locked:n,entitlements:r=[]}=e.entry;n&&r.length<=0&&(r=e.feedid?[e.feedid]:[]);let i=e.feedid?t[e.feedid]:!1,a=r.length>0?r.find(e=>t[e]):!0;return!i&&!a}const t={feed:{},rawStories:[],timelines:[],timelinesStates:{},themeURL:[],theme:{},darkTheme:{},timelineMode:`Single`,timelineType:`Timeline`,sourceURL:null,baseUrl:``,metadataLoaded:!1,initialBatchSize:0,filter:{},positionOverrides:[],debugLogs:!1,actionProviderNames:[],authorisationStatus:{},issueAuthorisationStatus:{},read:new Set,saved:new Set,get stories(){return n(this.rawStories,this.saved,this.read,this.authorisationStatus,this.issueAuthorisationStatus)},get themeFileNames(){return this.themeURL?.map(e=>e.split(`/`).pop()??``)??[]},get isSavedTimeline(){return this.timelineType===`SavedTimeline`},get isCollection(){return this.feed?.classes?.includes(`collection_type-edition`)??!1},isTimelineStyleVersion(e){return window.timelineStyleVersion===e}};function n(t,n,a,o,s){let c=!!window.parent?.pugpigBridgeService?.issueAuthorisationStatus;return t.map((t,l)=>(t.locked=c?e(t,s):r(t,o),t.hidden=i(t,o),t.saved=n.has(t.entry.id),t.read=a.has(`${t.feedid}::::${t.entry.id}`),t.originalIndex=l,t))}function r(e,t){let{state:n}=t,{paywall_locked:r=!1}=e.entry;return n!==`active`&&r}function i(e,t){let{state:n}=t,{visibility:r,categories:i,hidden:a}=e.entry,o=i?.find(({scheme:e})=>e===`http://schema.pugpig.com/attributes`);return r?r===`marketing`&&n!==`inactive`||r===`private`&&!!e.locked:o?o.term===`marketing`&&n!==`inactive`||o.term===`private`&&n!==`inactive`:!!a}async function a(e){try{let t=await e;return{data:t?JSON.parse(t):void 0}}catch(e){return{error:e}}}async function o(e,t){let n=window.parent?.pugpigBridgeService,r=n&&n[e],i=t?JSON.stringify(t):void 0;if(r){let{data:t,error:r}=await a(Promise.resolve(i?n[e](i):n[e]()));if(r)throw Error(r instanceof Error?r.message:JSON.stringify(r));return t}window.parent?.postMessage({bolt:`up`,action:e,payload:JSON.parse(i??`{}`)},`*`)}function s(){return(window.location.pathname.split(`/`).pop()?.replace(`.html`,``)??``).split(/[-_]/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function c(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>e.apply(this,r),t)}}const l={openArticle(e,t){return o(`viewArticle`,{...e,story:e,index:t})},openEdition(e,t,n=!1,r=!0){let i={editionId:e,skipTimeline:n,autoDownload:r};return t&&(i.partition=t),o(`openEdition`,i)},shouldViewWidget(e,t){return o(`shouldViewWidget`,{...e,story:e,index:t})},setSaved(e,t,n){return o(`setSaved`,{story:e,index:t,saved:n})},setRead(e,t){return o(`setRead`,{story:e,index:t})},shareStory(e,t){return o(`shareStory`,{story:e,index:t})},addToCalendar(e,t){return o(`addToCalendar`,{story:e,index:t})}},u={logInfo(e,t){if(e===`debug`&&!this.debugLogs)return;let n=this.feed?.id?` [${this.feed.id}]`:``;o(`logInfo`,`[${document.title||`Bolt Timeline`}]${n} ${t}`)},sendAnalytics(e,t,n=0,r={}){let i=s(),a=t?.originalBaseURL||t?.baseURL,o=typeof e==`object`&&e?e:{},c={type:e,story:t,index:n,param:t?.feedid,dimensions:r,...o},l=c.paramName||`pugpigEditionID`,d=this.timelineType===`SavedTimeline`?`/SavedTimeline`:`/Timeline`;return c.dimensions.pugpigEventOrigin=i,!c.paramName&&c?.param===c?.story?.entry.title&&(l=`pugpigPageName`),c?.story?.entry.url&&a&&(c.story.entry.url=new URL(c.story.entry.url,a).href),c?.story?.entry.shareurl&&a&&(c.story.entry.shareurl=new URL(c.story.entry.shareurl,a).href),u.sendAnalyticsEvent(c.type,d,c.param,l,c.dimensions,c.story?.entry,c.story?.feedid,c.index)},sendAnalyticsEvent(e,t,n=``,r=``,i={},a,c,l){let u={name:e,category:t,paramName:r,feedid:c,storyEntry:a,index:l,dimensions:i};return n&&n!==``&&(u.param=typeof n==`string`?n:JSON.stringify(n)),u.dimensions={...i,pugpigEventOrigin:s()},o(`trackAnalyticsEvent`,u)}},d={openAudio(e,t,n){return o(`playAudio`,{story:e,index:n,...t})},openImageGallery(e,t=0){return o(`openImageGallery`,{images:e,initialImage:t})}},f={fireToast(e,t){return o(`fireToast`,{type:e,action:t})},clearForwardTouchesWithin(){return o(`clearForwardTouchesWithin`)},forwardTouchesWithin:c(async function(e){return await this.clearForwardTouchesWithin(),o(`forwardTouchesWithin`,e&&Object.values(e))},300)},p={call(e,t){return o(e,t)},timelineIsReady(){let e=import.meta.env.VITE_GIT_VERSION;o(`timelineIsReady`,{version:e}),this.logInfo(`global`,`timelineIsReady took ${performance.now()}ms`)}},m={...l,...u,...d,...f,...p},h={events:{},debounceTimers:{},$on(e,t){return Array.isArray(this.events[e])||(this.events[e]=[]),this.events[e].push(t),()=>this.removeListener(e,t)},removeListener(e,t){if(Array.isArray(this.events[e])){let n=this.events[e].indexOf(t);n>-1&&this.events[e].splice(n,1)}},$off(e,t){this.removeListener(e,t)},$emit(e,...t){let n=t?.[0]?.debounce,r=Object.keys(this.events).filter(t=>RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(e));!r||r.length<=0||r.forEach(e=>{n?(this.debounceTimers[e]&&clearTimeout(this.debounceTimers[e]),this.debounceTimers[e]=setTimeout(()=>{this.events[e].forEach(e=>e.apply(this,t))},n)):this.events[e].forEach(e=>e.apply(this,t))})},$once(e,t){let n=this.$on(e,(...e)=>{n(),t.apply(this,e)})},subscribe(e){return e(this),this.$on(`change`,()=>e(this))}},g=Object.assign(t,{...m,resolveIssueAccess:n=>e(n,t.issueAuthorisationStatus)},h);function _(e){return new Proxy(e,{set(t,n,r,i){let a=Reflect.set(t,n,r,i);return typeof n==`string`?(e.$emit(`change:${n}`,r),e.$emit(`change`,n,r),a):a}})}function v(e,t){if(e.length!==t.length)return!0;for(let n=0;n<e.length;n++){let{entry:{id:r,updated:i}}=e[n],{entry:{id:a,updated:o}}=t[n];if(r!==a||i!==o)return!0}return!1}async function y(e,t){let n=window.parent?.pugpigBridgeService,r=t?await Promise.resolve(n?.[e](t)):await Promise.resolve(n?.[e]()),i=JSON.parse(r),a=window.parent?.pugpig?.timelineHooks?.transforms?.bridge;return a&&(i=await a({method:e,payload:i})),i}const b={stories:async function({updateAction:e}={}){let{stories:t}=await y(`stories`),n=this.rawStories,r=v(n,t);this.rawStories=t,r&&(window.pugpigUpdate(`issueAuthorisationStatus`),window.pugpigUpdate(`timelineInfo`)),n.length>0&&this.$emit(`stories-updated`,{feedUpdated:r,updateAction:e})},timelines:async function(){this.timelines=await y(`timelines`,JSON.stringify(this.filter))},timelineInfo:async function(){let{themeURL:e,timelineType:t,debugLogs:n,sourceURL:r,timelineMode:i,metadata:a,feedReference:o}=await y(`timelineInfo`);this.themeURL=e,this.sourceURL=r,this.theme=a?.configuration?.theme??{},this.darkTheme=a?.configuration?.darkTheme??this.theme,this.feed=o,this.timelineType=t,this.timelineMode=i,a&&(this.metadataLoaded=!0),this.initialBatchSize=a?.configuration?.timeline_initial_batch_size??a?.timeline_initial_batch_size??Math.floor(screen.height/150),window.useTimelineGrid&&(this.positionOverrides=window.timelinePositionOverrides??a?.configuration?.position_overrides??a?.position_overrides??[]),this.debugLogs=n},timeline:async function({editionId:e,progress:t,state:n,downloadError:r}){let i=this.timelines.findIndex(t=>t.id===e);!this.timelines||this.timelines.length<=0||!this.timelines[i]||(this.timelinesStates[e]||(this.timelinesStates[e]={}),this.timelinesStates[e].progress=t,this.timelinesStates[e].state=n,this.timelinesStates[e].downloadError=r)},timelinesInfo:async function(){let{baseurl:e,debugLogs:t,filter:n,css:r=[],theme:i={},darkTheme:a}=await y(`timelinesInfo`);this.theme=i,this.darkTheme=a??i,this.themeURL=r,this.baseUrl=e,this.filter=n,this.debugLogs=t},localeInfo:async function(){let{locale:e=`en-GB`,direction:t=`ltr`}=await y(`localeInfo`);document.documentElement.lang!==e&&(document.documentElement.lang=e?.replace(/_/g,`-`)),document.documentElement.dir!==t&&(document.documentElement.dir=t)},issueAuthorisationStatus:async function(){let e=[...new Set(this.rawStories.flatMap(({feedid:e,entry:t})=>[e,...t.entitlements??[]]))];this.issueAuthorisationStatus=await y(`issueAuthorisationStatus`,JSON.stringify(e))},authorisationStatus:async function(){this.authorisationStatus=await y(`authorisationStatus`)},actionProviderNames:async function(){this.actionProviderNames=await y(`actionProviderNames`)},readStories:async function(){let{readStories:e}=await y(`readStories`);this.read=new Set(e.map(e=>e.join(`::::`)))},savedStories:async function(){let{savedStories:e}=await y(`savedStories`);this.saved=new Set(e.map(e=>e?.[1]))},updateTime:async function(){let{dateTime:e,lastCheckedDateTime:t}=await y(`updateTime`);this.$emit(`time-updated`,{dateTime:e,lastCheckedDateTime:t})},storeGet:async function({key:e}){this.$emit(`store-updated`,{key:e})},webviewInfo:async function(){let{themeURL:e}=await y(`webviewInfo`);this.themeURL=e}};function x(e){return typeof e==`function`&&`__boltHandlers`in e}function S(){if(x(window.pugpigUpdate))return window.pugpigUpdate.__boltHandlers;let e=window.pugpigUpdate?[window.pugpigUpdate]:[],t=Object.assign((t,n={})=>e.forEach(e=>e(t,n)),{__boltHandlers:e});return window.pugpigUpdate=t,e}const C=new WeakMap;function w(e){let t=S();C.has(e)||C.set(e,(t,n={})=>{if(window.parent?.pugpigBridgeService?.[t])return document.querySelectorAll(`iframe`).forEach(e=>{try{e.contentWindow?.pugpigUpdate?.(t,n)}catch{}}),b[t]?.call(e,n);console.info(`Bolt Timeline Bridge: '${t}' not available on this platform.`)});let n=C.get(e);t.includes(n)||t.push(n)}const T=[`localeInfo`,`timelineInfo`,`authorisationStatus`,`savedStories`,`readStories`,`updateTime`,`actionProviderNames`,`stories`];function E(e=T){for(let t of e)window.pugpigUpdate(t)}function D(e,t={}){let{rootSelector:n=`#app`,readyTimeout:r=4e3}=t;`scrollRestoration`in history&&(history.scrollRestoration=`manual`),E();let i=document.querySelector(n),a=[],o=setTimeout(()=>{e.logInfo(`debug`,`${r}ms have passed, forcing timelineIsReady`),e.timelineIsReady()},r);function s(t){a.push(t),[`renderedRequiredStyles`,...e.themeFileNames??[]].every(e=>a.includes(e))&&(clearTimeout(o),document.fonts.ready.then(()=>e.timelineIsReady()))}for(let t of e.themeURL??[]){let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=t,e.onload=()=>s(t),e.onerror=()=>s(t),document.head.appendChild(e)}s(`renderedRequiredStyles`);function c(){let t=[...e.feed?.classes??[]];e.isSavedTimeline&&t.push(`saved-timeline`),i&&t.length&&i.classList.add(...t)}c();let l=e.$on(`change:feed`,c);return()=>{clearTimeout(o),l()}}export{_ as a,w as i,D as n,g as o,E as r,m as s,T as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t}from"./mockBridgeService-M_48ewQL.mjs";function n(e){return e===`origin`&&(e=window.location.origin),/^https?:\/\//i.test(e)||(e=`https://`+e),e}async function r(e){let n=`${e.live_domain}/timelines.json`,r=await t(n),i=r.timelines?.find(t=>t.id===e.edition)??r.timelines[0],a=new URL(i?.feed??``,n).href,o=await t(e?.stories||a);return Object.assign(e,{live_domain:e.live_domain,timelines:r.timelines,timeline:i,stories:o.stories,metadata:o.metadata})}async function i(e){let n=e.platform??`web`,r=(await t(`${e.live_domain}/bolt_properties.json`))?.[`${e.config}_version`]??`latest_version`,i=`${e.live_domain}/bolt/config/${r}/web/config.json`,a=(e.config?await t(i):void 0)?.timeline_css.map(t=>`${e.live_domain}/bolt/config/${r}/${n}/${t}`);return Object.assign(e,{themeUrl:a})}async function a(e,t){let a=new URLSearchParams(window.location.search),o=Object.fromEntries(a.entries());if(o.useStorage){let e=localStorage.getItem(`config`);if(e)return JSON.parse(e)}let s={...window.__TIMELINE_LOCAL_CONFIG__,...o,debugLogs:o.debugLogs===`true`};return s.live_domain&&(s.live_domain=n(s.live_domain),s=await r(s),s=await i(s)),localStorage.setItem(`config`,JSON.stringify(s)),s}async function o(t,n){window.config=await a(t,n),window.pugpigBridgeService=e(window.config)}export{o as injectBridge};
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { App } from "vue";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* @section Story types
|
|
6
|
-
*
|
|
7
|
-
* Core data structures for stories and their entries, shared across the bridge
|
|
8
|
-
* and widget layer.
|
|
9
|
-
*/
|
|
10
4
|
interface StoryImage {
|
|
11
5
|
src?: string;
|
|
12
6
|
srcset?: string;
|
|
@@ -15,7 +9,6 @@ interface StoryImage {
|
|
|
15
9
|
credit?: string;
|
|
16
10
|
externalurl?: string;
|
|
17
11
|
}
|
|
18
|
-
/** A single DFP ad size — either a [width, height] tuple or the string 'fluid'. */
|
|
19
12
|
type AdSize = number[] | string;
|
|
20
13
|
interface StoryEntry {
|
|
21
14
|
id: string;
|
|
@@ -155,62 +148,29 @@ interface Story {
|
|
|
155
148
|
isArticleLink?: boolean;
|
|
156
149
|
original_hidden?: boolean;
|
|
157
150
|
}
|
|
158
|
-
/**
|
|
159
|
-
* @section Bridge service interface
|
|
160
|
-
*
|
|
161
|
-
* The `BridgeService` interface consumed by `@pugpigbolt/components` and widgets to
|
|
162
|
-
* open articles, send analytics, manage saved state, and communicate with the
|
|
163
|
-
* native shell via events.
|
|
164
|
-
*/
|
|
165
151
|
interface BridgeService {
|
|
166
|
-
/** Current timeline type string, e.g. 'Timeline' | 'SavedTimeline'. */
|
|
167
152
|
timelineType: string;
|
|
168
|
-
/** Processed story array — locked, hidden, saved, and read flags applied. */
|
|
169
153
|
stories: Story[];
|
|
170
|
-
/** Unprocessed stories as received from the native shell before flag processing. */
|
|
171
154
|
rawStories: Story[];
|
|
172
|
-
/** Number of stories rendered in the first batch. */
|
|
173
155
|
initialBatchSize: number;
|
|
174
|
-
/** Position override rules applied to story ordering. */
|
|
175
156
|
positionOverrides: unknown[];
|
|
176
|
-
/** Names of registered native action providers, if any. */
|
|
177
157
|
actionProviderNames?: string[];
|
|
178
|
-
/** Current user authorisation status — state ('active'|'inactive'), token, userInfo. */
|
|
179
158
|
authorisationStatus?: AuthorisationStatus;
|
|
180
|
-
/** The source url that loaded the timeline. */
|
|
181
159
|
sourceURL?: string;
|
|
182
|
-
/** Returns true if the native app is running at least the given timeline style version. */
|
|
183
160
|
isTimelineStyleVersion(version: string): boolean;
|
|
184
|
-
/** Sends an analytics event. event is a string action name or a Record with type/param. story and index identify the context. */
|
|
185
161
|
sendAnalytics(event: string | Record<string, unknown>, story?: Story, index?: number, extra?: Record<string, unknown>): void;
|
|
186
|
-
/** Asks the native shell to open a story in the native article reader. */
|
|
187
162
|
openArticle(story: Story, index: number): void;
|
|
188
|
-
/** Marks a story as read in the native shell's read-state store. */
|
|
189
163
|
setRead(story: Story, index: number): void;
|
|
190
|
-
/** Opens the native audio player for a story. metadata carries player-specific config. */
|
|
191
164
|
openAudio(story: Story, metadata: Record<string, unknown>, index: number): void;
|
|
192
|
-
/** Opens the native share sheet for a story. */
|
|
193
165
|
shareStory(story: Story, index: number): void;
|
|
194
|
-
/** Saves or unsaves a story in the native shell's saved-stories store. */
|
|
195
166
|
setSaved(story: Story, index: number, saved: boolean): void;
|
|
196
|
-
/** Displays a native toast notification. key is the toast identifier, action is an optional CTA. */
|
|
197
167
|
fireToast(key: string, action?: string): void;
|
|
198
|
-
/** Returns whether the story is locked based on issue authorisation status. */
|
|
199
168
|
resolveIssueAccess(story: Pick<Story, 'feedid' | 'entry'>): boolean;
|
|
200
|
-
/** Adds an event-type story to the device calendar. */
|
|
201
169
|
addToCalendar(story: Story, index: number): void;
|
|
202
|
-
/** Dispatches an arbitrary named action to the native bridge. Use as an escape hatch when no typed method exists. */
|
|
203
170
|
call(method: string, ...args: unknown[]): Promise<unknown>;
|
|
204
|
-
/** Subscribes to a bridge event by name. Returns an unsubscribe function — call it in onUnmounted. */
|
|
205
171
|
$on(event: string, callback: (...args: unknown[]) => void): () => void;
|
|
206
|
-
/** Emits a named bridge event with an optional data payload. */
|
|
207
172
|
$emit(event: string, data?: unknown): void;
|
|
208
173
|
}
|
|
209
|
-
/**
|
|
210
|
-
* @section Timeline types
|
|
211
|
-
*
|
|
212
|
-
* Types describing timelines and their download or progress state.
|
|
213
|
-
*/
|
|
214
174
|
interface Timeline {
|
|
215
175
|
id: string;
|
|
216
176
|
feed?: string;
|
|
@@ -221,12 +181,6 @@ interface TimelineState {
|
|
|
221
181
|
state?: 'idle' | 'downloading' | 'deleting' | (string & {});
|
|
222
182
|
downloadError?: string;
|
|
223
183
|
}
|
|
224
|
-
/**
|
|
225
|
-
* @section Feed, auth, and theme
|
|
226
|
-
*
|
|
227
|
-
* Feed descriptor, authorisation status, issue-level access map, and theme
|
|
228
|
-
* types used across the bridge.
|
|
229
|
-
*/
|
|
230
184
|
interface Feed {
|
|
231
185
|
id?: string;
|
|
232
186
|
classes?: string[];
|
|
@@ -242,12 +196,6 @@ interface AuthorisationStatus {
|
|
|
242
196
|
}
|
|
243
197
|
type IssueAuthorisationStatus = Record<string, boolean>;
|
|
244
198
|
type Theme = Record<string, unknown>;
|
|
245
|
-
/**
|
|
246
|
-
* @section Remote config
|
|
247
|
-
*
|
|
248
|
-
* Timeline metadata and the full remote configuration shape delivered by the
|
|
249
|
-
* native shell at startup.
|
|
250
|
-
*/
|
|
251
199
|
interface TimelineMetadata {
|
|
252
200
|
configuration?: {
|
|
253
201
|
theme?: Theme;
|
|
@@ -275,15 +223,9 @@ interface RemoteConfig {
|
|
|
275
223
|
config?: string;
|
|
276
224
|
globalDir?: string;
|
|
277
225
|
baseurl?: string;
|
|
226
|
+
version?: string;
|
|
278
227
|
[key: string]: unknown;
|
|
279
228
|
}
|
|
280
|
-
/**
|
|
281
|
-
* @section Native bridge service interface
|
|
282
|
-
*
|
|
283
|
-
* The raw `parent.pugpigBridgeService` interface exposed by the native iOS and
|
|
284
|
-
* Android shell. Widget code should not call this directly — use `BridgeService`
|
|
285
|
-
* instead.
|
|
286
|
-
*/
|
|
287
229
|
interface PugpigBridgeService {
|
|
288
230
|
stories(payload?: string): unknown;
|
|
289
231
|
timeline(payload?: string): unknown;
|
|
@@ -317,12 +259,6 @@ interface PugpigBridgeService {
|
|
|
317
259
|
actionProviderNames?(payload?: string): unknown;
|
|
318
260
|
[key: string]: ((payload?: string) => unknown) | undefined;
|
|
319
261
|
}
|
|
320
|
-
/**
|
|
321
|
-
* @section Incoming handler payload types
|
|
322
|
-
*
|
|
323
|
-
* Payload shapes for each `pugpigUpdate` method call received from the native
|
|
324
|
-
* shell.
|
|
325
|
-
*/
|
|
326
262
|
interface StoriesPayload {
|
|
327
263
|
stories: Story[];
|
|
328
264
|
}
|
|
@@ -366,37 +302,11 @@ interface TimelineInfoPayload {
|
|
|
366
302
|
feedReference: Feed;
|
|
367
303
|
metadata: TimelineMetadata;
|
|
368
304
|
}
|
|
369
|
-
/**
|
|
370
|
-
* @section Bootstrap
|
|
371
|
-
*
|
|
372
|
-
* Options passed to the bridge bootstrap process that initialises the Vue app
|
|
373
|
-
* and connects it to the native shell.
|
|
374
|
-
*/
|
|
375
305
|
interface BootstrapOptions {
|
|
376
|
-
/**
|
|
377
|
-
* CSS selector for the app root element used to apply feed classes.
|
|
378
|
-
* Defaults to `'#app'`.
|
|
379
|
-
*/
|
|
380
306
|
rootSelector?: string;
|
|
381
|
-
/**
|
|
382
|
-
* Milliseconds to wait for theme CSS and fonts before forcing `timelineIsReady`.
|
|
383
|
-
* Defaults to `4000`.
|
|
384
|
-
*/
|
|
385
307
|
readyTimeout?: number;
|
|
386
308
|
}
|
|
387
|
-
/**
|
|
388
|
-
* @section Plugin type
|
|
389
|
-
*
|
|
390
|
-
* Type alias for the Vue plugin produced by the bridge factory. Pass an
|
|
391
|
-
* instance to `app.use()` to install the bridge into a Vue application.
|
|
392
|
-
*/
|
|
393
309
|
type BoltBridgePlugin = (app: App, callback?: () => void) => void;
|
|
394
|
-
/**
|
|
395
|
-
* @section Analytics
|
|
396
|
-
*
|
|
397
|
-
* Context and dimension types passed when firing analytics events through the
|
|
398
|
-
* bridge.
|
|
399
|
-
*/
|
|
400
310
|
interface AnalyticsContext {
|
|
401
311
|
debugLogs: boolean;
|
|
402
312
|
feed?: {
|
|
@@ -408,12 +318,6 @@ interface AnalyticsDimensions {
|
|
|
408
318
|
pugpigEventOrigin?: string;
|
|
409
319
|
[key: string]: unknown;
|
|
410
320
|
}
|
|
411
|
-
/**
|
|
412
|
-
* @section Global window augmentation
|
|
413
|
-
*
|
|
414
|
-
* Extensions added to the browser `window` object by the native shell,
|
|
415
|
-
* including the `pugpigUpdate` entry point and timeline configuration globals.
|
|
416
|
-
*/
|
|
417
321
|
declare global {
|
|
418
322
|
interface Window {
|
|
419
323
|
boltBridge?: unknown;
|
|
@@ -549,8 +453,8 @@ declare function createPugpigBridgeService(config: BridgeServiceConfig): {
|
|
|
549
453
|
ok: boolean;
|
|
550
454
|
};
|
|
551
455
|
css: string[] | undefined;
|
|
552
|
-
theme:
|
|
553
|
-
darkTheme:
|
|
456
|
+
theme: Record<string, unknown> | undefined;
|
|
457
|
+
darkTheme: Record<string, unknown> | undefined;
|
|
554
458
|
debugLogs: boolean | undefined;
|
|
555
459
|
};
|
|
556
460
|
storeGet(payload?: string): string | null;
|
|
@@ -587,15 +491,7 @@ declare const BOOTSTRAP_METHODS: readonly ["localeInfo", "timelineInfo", "author
|
|
|
587
491
|
declare function triggerPugpigUpdateSequence(methods?: readonly string[]): void;
|
|
588
492
|
//#endregion
|
|
589
493
|
//#region src/index.d.ts
|
|
590
|
-
/**
|
|
591
|
-
* @signature globalBridge: GlobalBridge
|
|
592
|
-
* @description Singleton proxy bridge instance exposed as window.boltBridge. Emits change:prop events on every property write. Use this for non-Vue integrations; Vue apps should use useBridge() instead.
|
|
593
|
-
*/
|
|
594
494
|
declare const globalBridge: GlobalBridge;
|
|
595
|
-
/**
|
|
596
|
-
* @signature bootstrap(options?: BootstrapOptions): () => void
|
|
597
|
-
* @description Initialises the bridge for a non-Vue framework entry point. Call once before rendering. Returns a teardown function that removes all listeners and cleans up state.
|
|
598
|
-
*/
|
|
599
495
|
declare function bootstrap(options?: BootstrapOptions): () => void;
|
|
600
496
|
//#endregion
|
|
601
497
|
export { TimelineState as A, StoryEntry as C, TimelineArgs as D, Timeline as E, UpdateTimePayload as M, WebviewInfoPayload as N, TimelineInfoPayload as O, Story as S, Theme as T, PugpigBridgeService as _, createPugpigBridgeService as a, SavedStoriesPayload as b, AnalyticsContext as c, BoltBridgePlugin as d, BootstrapOptions as f, LocaleInfoPayload as g, IssueAuthorisationStatus as h, triggerPugpigUpdateSequence as i, TimelinesInfoPayload as j, TimelineMetadata as k, AnalyticsDimensions as l, Feed as m, globalBridge as n, GlobalBridge as o, BridgeService as p, BOOTSTRAP_METHODS as r, AdSize as s, bootstrap as t, AuthorisationStatus as u, ReadStoriesPayload as v, StoryImage as w, StoriesPayload as x, RemoteConfig as y };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
async function e(e,t){let n=await(await fetch(e)).json();return t?n?.[t]:n}function t(e){let t=0,n=`idle`;window[e]&&clearInterval(window[e]),window[e]=setInterval(()=>{let r=t<=0,i=t>=1;n=r||i?`idle`:`downloading`,i&&clearInterval(window[e]),window.pugpigUpdate(`timeline`,{editionId:e,state:n,progress:t}),t+=.05},500)}let n=null;function r(){return n||(n=document.createElement(`div`),n.id=`bridge-toast-container`,n.style.cssText=`
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 10px;
|
|
4
|
+
right: 10px;
|
|
5
|
+
z-index: 99999;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 8px;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
`,document.body.appendChild(n),n)}function i(e,t=3e3){let n=r(),i=document.createElement(`div`);i.style.cssText=`
|
|
11
|
+
background: #333;
|
|
12
|
+
color: #fff;
|
|
13
|
+
padding: 8px 12px;
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
font-size: 12px;
|
|
16
|
+
font-family: monospace;
|
|
17
|
+
max-width: 300px;
|
|
18
|
+
word-break: break-word;
|
|
19
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
20
|
+
animation: slideIn 0.3s ease;
|
|
21
|
+
pointer-events: auto;
|
|
22
|
+
`,i.textContent=e;let a=document.createElement(`style`);a.textContent=`
|
|
23
|
+
@keyframes slideIn {
|
|
24
|
+
from { transform: translateX(100%); opacity: 0; }
|
|
25
|
+
to { transform: translateX(0); opacity: 1; }
|
|
26
|
+
}
|
|
27
|
+
`,document.querySelector(`#bridge-toast-styles`)||(a.id=`bridge-toast-styles`,document.head.appendChild(a)),n.appendChild(i),setTimeout(()=>{i.style.opacity=`0`,i.style.transition=`opacity 0.3s ease`,setTimeout(()=>i.remove(),300)},t)}function a(e){return{stories(){return window.stories=window.stories||o(e,e.stories),{stories:window.stories}},timeline(){return{progress:0,state:`idle`}},timelines(){return window.timelines=window.timelines||e.timelines,window.timelines},logInfo(e){return console.info(JSON.parse(e??`null`))},timelineInfo(){let{themeUrl:t,timelineType:n,metadata:r,debugLogs:i}=window.config??e,a=(window.config??e).timeline;return{themeURL:t,timelineType:n,debugLogs:i,metadata:r,sourceURL:window.sourceURL||window.location.href,feedReference:a}},forwardTouchesWithin(e){JSON.parse(e??`[]`).forEach(e=>{let t=document.createElement(`div`);t.className=`forwardTouchesWithin`,t.style.position=`absolute`,t.style.width=e.w+`px`,t.style.height=e.h+`px`,t.style.top=e.y+`px`,t.style.left=e.x+`px`,t.style.outline=`3px solid red`,t.style.pointerEvents=`none`,document.body.appendChild(t)})},clearForwardTouchesWithin(){document.querySelectorAll(`.forwardTouchesWithin`).forEach(e=>e.remove())},issueAuthorisationStatus(e){return JSON.parse(e??`[]`).reduce((e,t)=>(e[t]=!0,e),{})},shouldViewWidget(e){return!0},viewArticle(){console.log(`eheheh`)},startEditionDownload(e){let{editionId:n}=JSON.parse(e??`{}`);t(n)},deleteEdition(e){let{editionId:t}=JSON.parse(e??`{}`);window.pugpigUpdate(`timeline`,{editionId:t,state:`deleting`}),setTimeout(()=>window.pugpigUpdate(`timeline`,{editionId:t,state:`idle`,progress:0}),1e3)},cancelEditionDownload(e){let{editionId:t}=JSON.parse(e??`{}`);window[t]&&clearInterval(window[t]),window.pugpigUpdate(`timeline`,{editionId:t,state:`idle`,progress:0})},timelinesInfo(){let{theme:t,darkTheme:n,css:r,debugLogs:i,live_domain:a}=window.config??e;return{baseurl:a,filter:{ok:!0},css:r,theme:t,darkTheme:n,debugLogs:i}},storeGet(e){let t=JSON.parse(e??`null`);return localStorage.getItem(t)},storeSet(e){let{key:t,value:n}=JSON.parse(e??`{}`),r=localStorage.setItem(t,n);return setTimeout(()=>window.pugpigUpdate(`storeGet`,{key:t}),200),r},localeInfo(){let{globalDir:t}=window.config??e;return{locale:`en_GB`,direction:t}},trackAnalyticsEvent(e){return JSON.parse(e??`null`)},updateTime(){return{dateTime:Date.now(),lastCheckedDateTime:Date.now()}},authorisationStatus(e){return{state:`active`,userInfo:{}}},savedStories(){return{savedStories:window.saved||[]}},readStories(){return{readStories:window.read||[]}},localizableString(e){let{stringId:t,params:n}=JSON.parse(e??`{}`);return n?.length?`${t}(${n.join(`, `)})`:t},localizableRelativeTimeString(e){let{datetime:t}=JSON.parse(e??`{}`);return t?new Date(t).toLocaleDateString():null},localizableDateTimeString(e){let{datetime:t}=JSON.parse(e??`{}`);return t?new Date(t).toLocaleString():null},localizableQuantityString(e){let{stringId:t,quantity:n}=JSON.parse(e??`{}`);return`${t}(${n})`},setSaved(e){let{story:t,saved:n}=JSON.parse(e??`{}`);window.saved=window.saved||[];let r=window.saved.findIndex(([e,n])=>e===t.feedid&&n===t.entry.id);!n&&r>-1&&window.saved.splice(r,1),n&&window.saved.push([t.feedid,t.entry.id]),window.pugpigUpdate(`savedStories`)},setRead(e){let{story:t}=JSON.parse(e??`{}`);return window.read=window.read||[],window.read.push([t.feedid,t.entry.id]),window.pugpigUpdate(`readStories`),JSON.parse(e??`null`)}}}function o(e,t){return t.map(t=>({feedid:e?.timeline?.id||`Bolt Timeline`,entry:t,saved:!1,locked:!0,baseURL:e.live_domain}))}function s(e){let t=a(e);return new Proxy(t,{get(t,n){let r=t[n];return typeof r==`function`?(...t)=>{let a=r(t[0])??null;return e.debugLogs&&a&&(i(`${n} ${JSON.stringify(t)}`),console.log(`%c[Pugpig Bridge Service]%c[${n}]`,`color: green;`,`color: orange;`,n,r(t[0]))),a?JSON.stringify(a):void 0}:t[n]}})}export{s as n,e as r,a as t};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, a as createPugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, n as globalBridge, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, t as bootstrap, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./index-
|
|
1
|
+
import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, a as createPugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, n as globalBridge, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, t as bootstrap, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./chunks/index-BWVJLr0k.mjs";
|
|
2
2
|
export { AdSize, AnalyticsContext, AnalyticsDimensions, AuthorisationStatus, BOOTSTRAP_METHODS, BoltBridgePlugin, BootstrapOptions, BridgeService, Feed, GlobalBridge, IssueAuthorisationStatus, LocaleInfoPayload, PugpigBridgeService, ReadStoriesPayload, RemoteConfig, SavedStoriesPayload, StoriesPayload, Story, StoryEntry, StoryImage, Theme, Timeline, TimelineArgs, TimelineInfoPayload, TimelineMetadata, TimelineState, TimelinesInfoPayload, UpdateTimePayload, WebviewInfoPayload, bootstrap, createPugpigBridgeService, globalBridge, triggerPugpigUpdateSequence };
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { t as createPugpigBridgeService } from "./mockBridgeService-vnaAIjCu.mjs";
|
|
3
|
-
//#region src/index.ts
|
|
4
|
-
/**
|
|
5
|
-
* @signature globalBridge: GlobalBridge
|
|
6
|
-
* @description Singleton proxy bridge instance exposed as window.boltBridge. Emits change:prop events on every property write. Use this for non-Vue integrations; Vue apps should use useBridge() instead.
|
|
7
|
-
*/
|
|
8
|
-
const globalBridge = window.boltBridge || makeProxyBridge(rawBridge);
|
|
9
|
-
window.boltBridge = globalBridge;
|
|
10
|
-
setupPugpigUpdate(globalBridge);
|
|
11
|
-
/**
|
|
12
|
-
* @signature bootstrap(options?: BootstrapOptions): () => void
|
|
13
|
-
* @description Initialises the bridge for a non-Vue framework entry point. Call once before rendering. Returns a teardown function that removes all listeners and cleans up state.
|
|
14
|
-
*/
|
|
15
|
-
function bootstrap(options) {
|
|
16
|
-
return bootstrap$1(globalBridge, options);
|
|
17
|
-
}
|
|
18
|
-
//#endregion
|
|
19
|
-
export { BOOTSTRAP_METHODS, bootstrap, createPugpigBridgeService, globalBridge, triggerPugpigUpdateSequence };
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"./chunks/bootstrap-B7Gwb_jl.mjs";import{t as o}from"./chunks/mockBridgeService-M_48ewQL.mjs";const s=window.boltBridge||e(r);window.boltBridge=s,t(s);function c(e){return n(s,e)}export{a as BOOTSTRAP_METHODS,c as bootstrap,o as createPugpigBridgeService,s as globalBridge,i as triggerPugpigUpdateSequence};
|
package/dist/vue.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./index-
|
|
1
|
+
import { A as TimelineState, C as StoryEntry, D as TimelineArgs, E as Timeline, M as UpdateTimePayload, N as WebviewInfoPayload, O as TimelineInfoPayload, S as Story, T as Theme, _ as PugpigBridgeService, b as SavedStoriesPayload, c as AnalyticsContext, d as BoltBridgePlugin, f as BootstrapOptions, g as LocaleInfoPayload, h as IssueAuthorisationStatus, i as triggerPugpigUpdateSequence, j as TimelinesInfoPayload, k as TimelineMetadata, l as AnalyticsDimensions, m as Feed, o as GlobalBridge, p as BridgeService, r as BOOTSTRAP_METHODS, s as AdSize, u as AuthorisationStatus, v as ReadStoriesPayload, w as StoryImage, x as StoriesPayload, y as RemoteConfig } from "./chunks/index-BWVJLr0k.mjs";
|
|
2
2
|
import * as _$vue from "vue";
|
|
3
3
|
import { App } from "vue";
|
|
4
4
|
|
|
@@ -13,43 +13,6 @@ declare function registerToolTips({
|
|
|
13
13
|
}?: RegisterTooltipsOptions): void;
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/vue/useBridge.d.ts
|
|
16
|
-
/**
|
|
17
|
-
* @description Returns the reactive bridge service with all store data as refs and all outgoing action methods bound to the reactive store.
|
|
18
|
-
* @usage const { stories, authorisationStatus, openArticle, sendAnalytics, ... } = useBridge()
|
|
19
|
-
* @returns stories {Ref<Story[]>} Derived story array with locked, hidden, saved, and read flags applied
|
|
20
|
-
* @returns rawStories {Ref<Story[]>} Unprocessed raw stories as received from the bridge
|
|
21
|
-
* @returns feed {Ref<Feed>} Current feed metadata
|
|
22
|
-
* @returns timelines {Ref<Timeline[]>} Available timelines
|
|
23
|
-
* @returns timelinesStates {Ref<Record<string, TimelineState>>} Per-timeline state records
|
|
24
|
-
* @returns themeURL {Ref<string[]>} URLs for the active theme files
|
|
25
|
-
* @returns theme {Ref<Theme>} Active light theme data
|
|
26
|
-
* @returns darkTheme {Ref<Theme>} Active dark theme data
|
|
27
|
-
* @returns timelineMode {Ref<string>} Current timeline display mode
|
|
28
|
-
* @returns timelineType {Ref<string>} Current timeline type (e.g. 'Timeline' | 'SavedTimeline')
|
|
29
|
-
* @returns sourceURL {Ref<string | null>} Source URL of the current feed
|
|
30
|
-
* @returns baseUrl {Ref<string>} Base URL used to resolve relative paths
|
|
31
|
-
* @returns metadataLoaded {Ref<boolean>} Whether feed metadata has been loaded
|
|
32
|
-
* @returns initialBatchSize {Ref<number>} Number of stories in the first rendered batch
|
|
33
|
-
* @returns filter {Ref<Record<string, unknown>>} Active filter state
|
|
34
|
-
* @returns authorisationStatus {Ref<AuthorisationStatus>} Current user authorisation status
|
|
35
|
-
* @returns issueAuthorisationStatus {Ref<IssueAuthorisationStatus>} Per-issue authorisation status
|
|
36
|
-
* @returns openArticle {(story: Story, index: number) => Promise<unknown>} Opens a story in the native reader
|
|
37
|
-
* @returns shareStory {(story: Story, index: number) => Promise<unknown>} Triggers the native share sheet
|
|
38
|
-
* @returns setSaved {(story: Story, index: number, saved: boolean) => Promise<unknown>} Updates the saved state for a story
|
|
39
|
-
* @returns setRead {(story: Story, index: number) => Promise<unknown>} Marks a story as read
|
|
40
|
-
* @returns sendAnalytics {(type: string | Record<string, unknown>, story: Story, index?: number, dimensions?: AnalyticsDimensions) => Promise<unknown>} Sends an analytics event
|
|
41
|
-
* @returns call {(action: string, payload?: unknown) => Promise<unknown>} Dispatches an arbitrary bridge action
|
|
42
|
-
* @returns timelineIsReady {() => void} Signals that the Vue app has mounted
|
|
43
|
-
* @returns fireToast {(type: string, action: string) => Promise<unknown>} Displays a native toast notification
|
|
44
|
-
* @returns openAudio {(story: Story, fallback: Record<string, unknown>, index: number) => Promise<unknown>} Opens the native audio player
|
|
45
|
-
* @returns openImageGallery {(images: StoryImage[] | undefined, initialImage?: number) => Promise<unknown>} Opens the native image gallery
|
|
46
|
-
* @returns addToCalendar {(story: Story, index: number) => Promise<unknown>} Adds an event to the device calendar
|
|
47
|
-
* @returns resolveIssueAccess {(story: Story) => boolean} Resolves whether a story is locked based on issue authorisation
|
|
48
|
-
* @returns $on {(event: string, listener: (...args: unknown[]) => void) => () => void} Subscribes to a bridge event; returns an unsubscribe function
|
|
49
|
-
* @returns $emit {(event: string, ...args: unknown[]) => void} Emits a bridge event
|
|
50
|
-
* @returns $once {(event: string, listener: (...args: unknown[]) => void) => void} Subscribes to a bridge event for a single invocation
|
|
51
|
-
* @returns bridge {BridgeService} The raw reactive bridge instance for direct access
|
|
52
|
-
*/
|
|
53
16
|
declare function useBridge(): {
|
|
54
17
|
resolveIssueAccess: (story: Story) => boolean;
|
|
55
18
|
$on: (event: string, listener: (...args: unknown[]) => void) => () => void;
|
|
@@ -77,28 +40,28 @@ declare function useBridge(): {
|
|
|
77
40
|
setRead(story: Story, index: number): Promise<unknown>;
|
|
78
41
|
shareStory(story: Story, index: number): Promise<unknown>;
|
|
79
42
|
addToCalendar(story: Story, index: number): Promise<unknown>;
|
|
43
|
+
authorisationStatus: _$vue.Ref<AuthorisationStatus, AuthorisationStatus>;
|
|
44
|
+
actionProviderNames: _$vue.Ref<string[], string[]>;
|
|
45
|
+
stories: _$vue.Ref<Story[], Story[]>;
|
|
46
|
+
timelineType: _$vue.Ref<string, string>;
|
|
47
|
+
debugLogs: _$vue.Ref<boolean, boolean>;
|
|
48
|
+
theme: _$vue.Ref<Theme, Theme>;
|
|
49
|
+
darkTheme: _$vue.Ref<Theme, Theme>;
|
|
80
50
|
filter: _$vue.Ref<Record<string, unknown>, Record<string, unknown>>;
|
|
81
51
|
feed: _$vue.Ref<Feed, Feed>;
|
|
82
52
|
rawStories: _$vue.Ref<Story[], Story[]>;
|
|
83
53
|
timelines: _$vue.Ref<Timeline[], Timeline[]>;
|
|
84
54
|
timelinesStates: _$vue.Ref<Record<string, TimelineState>, Record<string, TimelineState>>;
|
|
85
55
|
themeURL: _$vue.Ref<string[], string[]>;
|
|
86
|
-
theme: _$vue.Ref<Theme, Theme>;
|
|
87
|
-
darkTheme: _$vue.Ref<Theme, Theme>;
|
|
88
56
|
timelineMode: _$vue.Ref<string, string>;
|
|
89
|
-
timelineType: _$vue.Ref<string, string>;
|
|
90
57
|
sourceURL: _$vue.Ref<string | null, string | null>;
|
|
91
58
|
baseUrl: _$vue.Ref<string, string>;
|
|
92
59
|
metadataLoaded: _$vue.Ref<boolean, boolean>;
|
|
93
60
|
initialBatchSize: _$vue.Ref<number, number>;
|
|
94
61
|
positionOverrides: _$vue.Ref<unknown[], unknown[]>;
|
|
95
|
-
debugLogs: _$vue.Ref<boolean, boolean>;
|
|
96
|
-
actionProviderNames: _$vue.Ref<string[], string[]>;
|
|
97
|
-
authorisationStatus: _$vue.Ref<AuthorisationStatus, AuthorisationStatus>;
|
|
98
62
|
issueAuthorisationStatus: _$vue.Ref<IssueAuthorisationStatus, IssueAuthorisationStatus>;
|
|
99
63
|
read: _$vue.Ref<Set<string>, Set<string>>;
|
|
100
64
|
saved: _$vue.Ref<Set<string>, Set<string>>;
|
|
101
|
-
stories: _$vue.Ref<Story[], Story[]>;
|
|
102
65
|
themeFileNames: _$vue.Ref<string[], string[]>;
|
|
103
66
|
isSavedTimeline: _$vue.Ref<boolean, boolean>;
|
|
104
67
|
isCollection: _$vue.Ref<boolean, boolean>;
|
package/dist/vue.mjs
CHANGED
|
@@ -1,213 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { onUnmounted, reactive, ref, toRefs, watchEffect } from "vue";
|
|
3
|
-
//#region src/vue/tooltip.ts
|
|
4
|
-
const tooltipsQueue = ref([]);
|
|
5
|
-
const tooltipsRequests = ref([]);
|
|
6
|
-
const isScrolling = ref(false);
|
|
7
|
-
const createIntersectionObserver = (id, element) => {
|
|
8
|
-
if (tooltipsRequests.value.includes(id)) return;
|
|
9
|
-
const observer = new IntersectionObserver(([entry]) => {
|
|
10
|
-
if (!entry.isIntersecting) return;
|
|
11
|
-
if (tooltipsRequests.value.includes(id)) return observer.disconnect();
|
|
12
|
-
tooltipsQueue.value.push({
|
|
13
|
-
id,
|
|
14
|
-
el: element
|
|
15
|
-
});
|
|
16
|
-
observer.disconnect();
|
|
17
|
-
}, { threshold: 1 });
|
|
18
|
-
observer.observe(element);
|
|
19
|
-
return observer;
|
|
20
|
-
};
|
|
21
|
-
function initToolTips() {
|
|
22
|
-
window.addEventListener("scroll", () => {
|
|
23
|
-
isScrolling.value = true;
|
|
24
|
-
window.clearTimeout(window.scrollTimeout);
|
|
25
|
-
window.scrollTimeout = setTimeout(() => isScrolling.value = false, 150);
|
|
26
|
-
}, false);
|
|
27
|
-
watchEffect(() => {
|
|
28
|
-
if (isScrolling.value) return;
|
|
29
|
-
tooltipsQueue.value.forEach((tooltip, index) => {
|
|
30
|
-
if (tooltipsRequests.value.includes(tooltip.id)) return;
|
|
31
|
-
const { x, y, width: w, height: h } = tooltip.el.getBoundingClientRect();
|
|
32
|
-
const source = {
|
|
33
|
-
w,
|
|
34
|
-
h,
|
|
35
|
-
x,
|
|
36
|
-
y: y + window.scrollY
|
|
37
|
-
};
|
|
38
|
-
window.parent?.pugpigBridgeService?.showToolTip?.(JSON.stringify({
|
|
39
|
-
id: tooltip.id,
|
|
40
|
-
source
|
|
41
|
-
}));
|
|
42
|
-
tooltipsQueue.value.splice(index, 1);
|
|
43
|
-
tooltipsRequests.value.push(tooltip.id);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
function registerToolTips({ attribute = "tooltip", observerOptions } = {}) {
|
|
48
|
-
if (!window.parent?.pugpigBridgeService?.showToolTip) return;
|
|
49
|
-
initToolTips();
|
|
50
|
-
const mutationObserverCallback = (mutations) => {
|
|
51
|
-
mutations.forEach((mutation) => mutation.addedNodes.forEach((node) => {
|
|
52
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
53
|
-
const el = node;
|
|
54
|
-
const tooltipValue = el.getAttribute(attribute);
|
|
55
|
-
if (!tooltipValue) return;
|
|
56
|
-
if (tooltipsRequests.value.includes(tooltipValue)) return;
|
|
57
|
-
createIntersectionObserver(tooltipValue, el);
|
|
58
|
-
}));
|
|
59
|
-
};
|
|
60
|
-
const mutationObserver = new MutationObserver(mutationObserverCallback);
|
|
61
|
-
mutationObserverCallback([{ addedNodes: document.body.childNodes }]);
|
|
62
|
-
mutationObserver.observe(document.body, observerOptions ?? {
|
|
63
|
-
childList: true,
|
|
64
|
-
subtree: true
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
function installTooltipDirective(app) {
|
|
68
|
-
if (!window.parent?.pugpigBridgeService?.showToolTip) {
|
|
69
|
-
app.directive("tooltip", {});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
initToolTips();
|
|
73
|
-
app.directive("tooltip", { mounted: (el, binding) => createIntersectionObserver(binding.value, el) });
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/vue/useBridge.ts
|
|
77
|
-
const reactiveBridge = window.boltBridge || reactive(rawBridge);
|
|
78
|
-
function bindAll(obj, ctx) {
|
|
79
|
-
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, typeof v === "function" ? v.bind(ctx) : v]));
|
|
80
|
-
}
|
|
81
|
-
const boundActions = bindAll(outgoingActions, reactiveBridge);
|
|
82
|
-
/**
|
|
83
|
-
* @description Returns the reactive bridge service with all store data as refs and all outgoing action methods bound to the reactive store.
|
|
84
|
-
* @usage const { stories, authorisationStatus, openArticle, sendAnalytics, ... } = useBridge()
|
|
85
|
-
* @returns stories {Ref<Story[]>} Derived story array with locked, hidden, saved, and read flags applied
|
|
86
|
-
* @returns rawStories {Ref<Story[]>} Unprocessed raw stories as received from the bridge
|
|
87
|
-
* @returns feed {Ref<Feed>} Current feed metadata
|
|
88
|
-
* @returns timelines {Ref<Timeline[]>} Available timelines
|
|
89
|
-
* @returns timelinesStates {Ref<Record<string, TimelineState>>} Per-timeline state records
|
|
90
|
-
* @returns themeURL {Ref<string[]>} URLs for the active theme files
|
|
91
|
-
* @returns theme {Ref<Theme>} Active light theme data
|
|
92
|
-
* @returns darkTheme {Ref<Theme>} Active dark theme data
|
|
93
|
-
* @returns timelineMode {Ref<string>} Current timeline display mode
|
|
94
|
-
* @returns timelineType {Ref<string>} Current timeline type (e.g. 'Timeline' | 'SavedTimeline')
|
|
95
|
-
* @returns sourceURL {Ref<string | null>} Source URL of the current feed
|
|
96
|
-
* @returns baseUrl {Ref<string>} Base URL used to resolve relative paths
|
|
97
|
-
* @returns metadataLoaded {Ref<boolean>} Whether feed metadata has been loaded
|
|
98
|
-
* @returns initialBatchSize {Ref<number>} Number of stories in the first rendered batch
|
|
99
|
-
* @returns filter {Ref<Record<string, unknown>>} Active filter state
|
|
100
|
-
* @returns authorisationStatus {Ref<AuthorisationStatus>} Current user authorisation status
|
|
101
|
-
* @returns issueAuthorisationStatus {Ref<IssueAuthorisationStatus>} Per-issue authorisation status
|
|
102
|
-
* @returns openArticle {(story: Story, index: number) => Promise<unknown>} Opens a story in the native reader
|
|
103
|
-
* @returns shareStory {(story: Story, index: number) => Promise<unknown>} Triggers the native share sheet
|
|
104
|
-
* @returns setSaved {(story: Story, index: number, saved: boolean) => Promise<unknown>} Updates the saved state for a story
|
|
105
|
-
* @returns setRead {(story: Story, index: number) => Promise<unknown>} Marks a story as read
|
|
106
|
-
* @returns sendAnalytics {(type: string | Record<string, unknown>, story: Story, index?: number, dimensions?: AnalyticsDimensions) => Promise<unknown>} Sends an analytics event
|
|
107
|
-
* @returns call {(action: string, payload?: unknown) => Promise<unknown>} Dispatches an arbitrary bridge action
|
|
108
|
-
* @returns timelineIsReady {() => void} Signals that the Vue app has mounted
|
|
109
|
-
* @returns fireToast {(type: string, action: string) => Promise<unknown>} Displays a native toast notification
|
|
110
|
-
* @returns openAudio {(story: Story, fallback: Record<string, unknown>, index: number) => Promise<unknown>} Opens the native audio player
|
|
111
|
-
* @returns openImageGallery {(images: StoryImage[] | undefined, initialImage?: number) => Promise<unknown>} Opens the native image gallery
|
|
112
|
-
* @returns addToCalendar {(story: Story, index: number) => Promise<unknown>} Adds an event to the device calendar
|
|
113
|
-
* @returns resolveIssueAccess {(story: Story) => boolean} Resolves whether a story is locked based on issue authorisation
|
|
114
|
-
* @returns $on {(event: string, listener: (...args: unknown[]) => void) => () => void} Subscribes to a bridge event; returns an unsubscribe function
|
|
115
|
-
* @returns $emit {(event: string, ...args: unknown[]) => void} Emits a bridge event
|
|
116
|
-
* @returns $once {(event: string, listener: (...args: unknown[]) => void) => void} Subscribes to a bridge event for a single invocation
|
|
117
|
-
* @returns bridge {BridgeService} The raw reactive bridge instance for direct access
|
|
118
|
-
*/
|
|
119
|
-
function useBridge() {
|
|
120
|
-
return {
|
|
121
|
-
...toRefs(reactiveBridge),
|
|
122
|
-
...boundActions,
|
|
123
|
-
resolveIssueAccess: reactiveBridge.resolveIssueAccess,
|
|
124
|
-
$on: (event, listener) => reactiveBridge.$on(event, listener),
|
|
125
|
-
$emit: (event, ...args) => reactiveBridge.$emit(event, ...args),
|
|
126
|
-
$once: (event, listener) => reactiveBridge.$once(event, listener),
|
|
127
|
-
bridge: reactiveBridge
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/vue/bootstrap.ts
|
|
132
|
-
function useBootstrap(options = {}) {
|
|
133
|
-
const { readyTimeout = 4e3 } = options;
|
|
134
|
-
if ("scrollRestoration" in history) history.scrollRestoration = "manual";
|
|
135
|
-
triggerPugpigUpdateSequence();
|
|
136
|
-
injectThemeAndWait(readyTimeout);
|
|
137
|
-
applyFeedClasses();
|
|
138
|
-
}
|
|
139
|
-
function injectThemeAndWait(readyTimeout = 4e3) {
|
|
140
|
-
const loaded = [];
|
|
141
|
-
const forceReady = setTimeout(() => {
|
|
142
|
-
globalBridge.logInfo?.("debug", `${readyTimeout}ms have passed, forcing timelineIsReady`);
|
|
143
|
-
globalBridge.timelineIsReady?.();
|
|
144
|
-
}, readyTimeout);
|
|
145
|
-
function onDepLoaded(name) {
|
|
146
|
-
loaded.push(name);
|
|
147
|
-
if (!["renderedRequiredStyles", ...globalBridge.themeFileNames ?? []].every((dep) => loaded.includes(dep))) return;
|
|
148
|
-
clearTimeout(forceReady);
|
|
149
|
-
document.fonts.ready.then(() => globalBridge.timelineIsReady?.());
|
|
150
|
-
}
|
|
151
|
-
for (const url of globalBridge.themeURL ?? []) {
|
|
152
|
-
const link = document.createElement("link");
|
|
153
|
-
link.rel = "stylesheet";
|
|
154
|
-
link.href = url;
|
|
155
|
-
link.onload = () => onDepLoaded(url);
|
|
156
|
-
link.onerror = () => onDepLoaded(url);
|
|
157
|
-
document.head.appendChild(link);
|
|
158
|
-
}
|
|
159
|
-
onDepLoaded("renderedRequiredStyles");
|
|
160
|
-
}
|
|
161
|
-
function applyFeedClasses(rootSelector = "#app") {
|
|
162
|
-
const rootEl = document.querySelector(rootSelector);
|
|
163
|
-
const classes = [...globalBridge.feed?.classes ?? []];
|
|
164
|
-
if (globalBridge.isSavedTimeline) classes.push("saved-timeline");
|
|
165
|
-
if (rootEl && classes.length) rootEl.classList.add(...classes);
|
|
166
|
-
}
|
|
167
|
-
function useDependencyTracker(options = {}) {
|
|
168
|
-
const { readyTimeout = 4e3 } = options;
|
|
169
|
-
const loadedDependencies = ref([]);
|
|
170
|
-
const forceReady = setTimeout(() => {
|
|
171
|
-
globalBridge.logInfo?.("debug", `${readyTimeout}ms have passed, forcing timelineIsReady`);
|
|
172
|
-
globalBridge.timelineIsReady?.();
|
|
173
|
-
}, readyTimeout);
|
|
174
|
-
function onDependencyLoaded(dependencyName) {
|
|
175
|
-
if (loadedDependencies.value.includes(dependencyName)) return;
|
|
176
|
-
loadedDependencies.value.push(dependencyName);
|
|
177
|
-
const isLoaded = [
|
|
178
|
-
"mainView",
|
|
179
|
-
"renderedRequiredStyles",
|
|
180
|
-
...globalBridge.themeFileNames ?? []
|
|
181
|
-
].every((dep) => loadedDependencies.value.includes(dep));
|
|
182
|
-
globalBridge.logInfo?.("debug", `Theme dependency '${dependencyName}' loaded (${performance.now()}ms)`);
|
|
183
|
-
if (!isLoaded) return;
|
|
184
|
-
clearTimeout(forceReady);
|
|
185
|
-
globalBridge.logInfo?.("debug", `Theme dependencies complete: ${loadedDependencies.value.join(", ")}`);
|
|
186
|
-
document.fonts.ready.then(() => {
|
|
187
|
-
globalBridge.logInfo?.("debug", `Document fonts loaded (${performance.now()}ms)`);
|
|
188
|
-
globalBridge.timelineIsReady?.();
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
onUnmounted(() => {
|
|
192
|
-
clearTimeout(forceReady);
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
loadedDependencies,
|
|
196
|
-
onDependencyLoaded
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
//#endregion
|
|
200
|
-
//#region src/vue/index.ts
|
|
201
|
-
const globalBridge = window.boltBridge || reactive(rawBridge);
|
|
202
|
-
window.boltBridge = globalBridge;
|
|
203
|
-
const isDebug = window.location.search.includes("preview");
|
|
204
|
-
const BoltBridgeVue = (app, callback) => {
|
|
205
|
-
setupPugpigUpdate(globalBridge);
|
|
206
|
-
app.config.globalProperties.$bridge = globalBridge;
|
|
207
|
-
app.provide("globalBridge", globalBridge);
|
|
208
|
-
app.use(installTooltipDirective);
|
|
209
|
-
if (isDebug) import("./development-Bl8-5E3g.mjs").then(({ injectBridge }) => injectBridge(app, callback)).then(() => callback?.());
|
|
210
|
-
else callback?.();
|
|
211
|
-
};
|
|
212
|
-
//#endregion
|
|
213
|
-
export { BOOTSTRAP_METHODS, BoltBridgeVue, applyFeedClasses, globalBridge, injectThemeAndWait, isDebug, registerToolTips, triggerPugpigUpdateSequence, useBootstrap, useBridge, useDependencyTracker };
|
|
1
|
+
import{i as e,o as t,r as n,s as r,t as i}from"./chunks/bootstrap-B7Gwb_jl.mjs";import{onUnmounted as a,reactive as o,ref as s,toRefs as c,watchEffect as l}from"vue";const u=s([]),d=s([]),f=s(!1),p=(e,t)=>{if(d.value.includes(e))return;let n=new IntersectionObserver(([r])=>{if(r.isIntersecting){if(d.value.includes(e))return n.disconnect();u.value.push({id:e,el:t}),n.disconnect()}},{threshold:1});return n.observe(t),n};function m(){window.addEventListener(`scroll`,()=>{f.value=!0,window.clearTimeout(window.scrollTimeout),window.scrollTimeout=setTimeout(()=>f.value=!1,150)},!1),l(()=>{f.value||u.value.forEach((e,t)=>{if(d.value.includes(e.id))return;let{x:n,y:r,width:i,height:a}=e.el.getBoundingClientRect(),o={w:i,h:a,x:n,y:r+window.scrollY};window.parent?.pugpigBridgeService?.showToolTip?.(JSON.stringify({id:e.id,source:o})),u.value.splice(t,1),d.value.push(e.id)})})}function h({attribute:e=`tooltip`,observerOptions:t}={}){if(!window.parent?.pugpigBridgeService?.showToolTip)return;m();let n=t=>{t.forEach(t=>t.addedNodes.forEach(t=>{if(t.nodeType!==Node.ELEMENT_NODE)return;let n=t,r=n.getAttribute(e);r&&(d.value.includes(r)||p(r,n))}))},r=new MutationObserver(n);n([{addedNodes:document.body.childNodes}]),r.observe(document.body,t??{childList:!0,subtree:!0})}function g(e){if(!window.parent?.pugpigBridgeService?.showToolTip){e.directive(`tooltip`,{});return}m(),e.directive(`tooltip`,{mounted:(e,t)=>p(t.value,e)})}const _=window.boltBridge||o(t);function v(e,t){return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,typeof n==`function`?n.bind(t):n]))}const y=v(r,_);function b(){return{...c(_),...y,resolveIssueAccess:_.resolveIssueAccess,$on:(e,t)=>_.$on(e,t),$emit:(e,...t)=>_.$emit(e,...t),$once:(e,t)=>_.$once(e,t),bridge:_}}function x(e={}){let{readyTimeout:t=4e3}=e;`scrollRestoration`in history&&(history.scrollRestoration=`manual`),n(),S(t),C()}function S(e=4e3){let t=[],n=setTimeout(()=>{T.logInfo?.(`debug`,`${e}ms have passed, forcing timelineIsReady`),T.timelineIsReady?.()},e);function r(e){t.push(e),[`renderedRequiredStyles`,...T.themeFileNames??[]].every(e=>t.includes(e))&&(clearTimeout(n),document.fonts.ready.then(()=>T.timelineIsReady?.()))}for(let e of T.themeURL??[]){let t=document.createElement(`link`);t.rel=`stylesheet`,t.href=e,t.onload=()=>r(e),t.onerror=()=>r(e),document.head.appendChild(t)}r(`renderedRequiredStyles`)}function C(e=`#app`){let t=document.querySelector(e),n=[...T.feed?.classes??[]];T.isSavedTimeline&&n.push(`saved-timeline`),t&&n.length&&t.classList.add(...n)}function w(e={}){let{readyTimeout:t=4e3}=e,n=s([]),r=setTimeout(()=>{T.logInfo?.(`debug`,`${t}ms have passed, forcing timelineIsReady`),T.timelineIsReady?.()},t);function i(e){if(n.value.includes(e))return;n.value.push(e);let t=[`mainView`,`renderedRequiredStyles`,...T.themeFileNames??[]].every(e=>n.value.includes(e));T.logInfo?.(`debug`,`Theme dependency '${e}' loaded (${performance.now()}ms)`),t&&(clearTimeout(r),T.logInfo?.(`debug`,`Theme dependencies complete: ${n.value.join(`, `)}`),document.fonts.ready.then(()=>{T.logInfo?.(`debug`,`Document fonts loaded (${performance.now()}ms)`),T.timelineIsReady?.()}))}return a(()=>{clearTimeout(r)}),{loadedDependencies:n,onDependencyLoaded:i}}const T=window.boltBridge||o(t);window.boltBridge=T;const E=window.location.search.includes(`preview`),D=(t,n)=>{e(T),t.config.globalProperties.$bridge=T,t.provide(`globalBridge`,T),t.use(g),E?import(`./chunks/development-CzUmtUQR.mjs`).then(({injectBridge:e})=>e(t,n)).then(()=>n?.()):n?.()};export{i as BOOTSTRAP_METHODS,D as BoltBridgeVue,C as applyFeedClasses,T as globalBridge,S as injectThemeAndWait,E as isDebug,h as registerToolTips,n as triggerPugpigUpdateSequence,x as useBootstrap,b as useBridge,w as useDependencyTracker};
|