samandesk 1.2.0 → 2.0.0-beta2.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.
@@ -1,229 +1,312 @@
1
- let token;
2
- const frame = document.createElement("iframe");
3
- let theme;
4
- let color;
5
- let language;
6
- let linkHandler = (link) => window.open(link, "_blank", "noopener");
7
- const backdrop = document.createElement("div");
8
- const run = {
9
- count: 0,
10
- promises: {}
11
- };
12
- function getRun() {
13
- return run;
14
- }
15
- function setToken(newToken) {
16
- token = newToken;
17
- }
18
- function getToken() {
19
- return token;
20
- }
21
- function clearFrame() {
22
- frame.src = "";
23
- }
24
- function setFrameSrc(src) {
25
- frame.src = src;
26
- }
27
- function getFrame() {
28
- return frame;
29
- }
30
- function getTheme() {
31
- return { theme, color };
32
- }
33
- function getLanguage() {
34
- return { language };
35
- }
36
- function setTheme(newTheme) {
37
- theme = newTheme.darkMode;
38
- color = newTheme.color;
39
- }
40
- function setLanguage(newLanguage) {
41
- language = newLanguage;
42
- }
43
- function getBackdrop() {
44
- return backdrop;
45
- }
46
- function setOpenLinkFn(fn) {
47
- if (fn) linkHandler = fn;
48
- }
49
- function getOpenLinkFn() {
50
- return linkHandler;
51
- }
52
- const readyEvent = new Event("ready");
53
- const openEvent = new Event("open");
54
- const closeEvent = new Event("close");
55
- const guestReadyEvent = new Event("guest-ready");
56
- function getReadyEvent() {
57
- return readyEvent;
58
- }
59
- function getOpenEvent() {
60
- return openEvent;
61
- }
62
- function getCloseEvent() {
63
- return closeEvent;
64
- }
65
- function setGuestReadyEvent() {
66
- return guestReadyEvent;
67
- }
68
- function handleEvents(interoperation) {
69
- window.addEventListener("message", (event) => {
70
- if (!event.data.from || event.data.from !== "SamanDesk") return;
71
- let handledByInterOperation = false;
72
- console.log(`message from (${event.origin})`, event);
73
- try {
74
- interoperation[event.data.action](event.data);
75
- handledByInterOperation = true;
76
- } catch (err) {
77
- console.error("error while run event action");
78
- }
79
- const data = event.data;
80
- const id = data.id;
81
- const run2 = getRun();
82
- if (id in run2.promises && run2.promises[id]) {
83
- run2.promises[id](data);
84
- delete run2.promises[id];
85
- } else if (!handledByInterOperation) {
86
- console.error("Message from outside of frame is invalid");
87
- }
88
- });
89
- }
90
- function resolveRun(id, data, action) {
91
- var _a;
92
- const postData = { from: "SamanDesk", action, data, id };
93
- const frame2 = getFrame();
94
- (_a = frame2.contentWindow) == null ? void 0 : _a.postMessage(postData, "*");
95
- }
96
- class InterOperation {
97
- async init() {
98
- handleEvents(this);
1
+ const __vite_import_meta_env__ = {};
2
+ class SamanDeskCore {
3
+ constructor(config) {
4
+ this.runPromises = {};
5
+ this.promiseCount = 0;
6
+ this.isReady = false;
7
+ this.config = {
8
+ siteUrl: this.defaultSiteUrl,
9
+ language: "fa",
10
+ theme: { darkMode: "light", color: "default" },
11
+ ...config
12
+ };
13
+ }
14
+ get defaultSiteUrl() {
15
+ return (__vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_DEV_MODE) === "1" ? "http://localhost:2525" : "https://samandesk.com";
16
+ }
17
+ getInitialUrl() {
18
+ if (this.config.verifiedToken) {
19
+ return `${this.config.siteUrl}/setup-interoperation/${this.config.verifiedToken}`;
20
+ }
21
+ return `${this.config.siteUrl}/guest-login/${this.config.workspaceID}`;
22
+ }
23
+ getOpenUrl(link) {
24
+ return link || `${this.config.siteUrl}/department-selection?token=${this.token}`;
25
+ }
26
+ handleIncomingMessage(message) {
27
+ if (!message || message.from !== "SamanDesk") return;
28
+ const { action, id, data } = message;
29
+ if (id !== void 0 && this.runPromises[id]) {
30
+ this.runPromises[id](data);
31
+ delete this.runPromises[id];
32
+ return;
33
+ }
34
+ switch (action) {
35
+ case "setToken":
36
+ this.handleSetToken(data);
37
+ break;
38
+ case "getConfig":
39
+ this.handleGetConfig(id);
40
+ break;
41
+ case "openLink":
42
+ this.handleOpenLink(data);
43
+ break;
44
+ case "setGuestReadyEvent":
45
+ this.handleSetGuestReadyEvent();
46
+ break;
47
+ case "close":
48
+ this.handleCloseFromInside();
49
+ break;
50
+ default:
51
+ console.warn(`[SamanDesk] Unhandled action: ${action}`);
52
+ }
53
+ }
54
+ handleSetToken(data) {
55
+ if (data && data.token) {
56
+ this.token = data.token;
57
+ this.isReady = true;
58
+ if (this.config.onReady) {
59
+ this.config.onReady(this.token);
60
+ }
61
+ if (this.config.onTokenChange) {
62
+ this.config.onTokenChange(this.token);
63
+ }
64
+ }
65
+ }
66
+ handleGetConfig(id) {
67
+ var _a, _b;
68
+ if (id !== void 0) {
69
+ this.sendResponse(id, "getConfig", {
70
+ color: ((_a = this.config.theme) == null ? void 0 : _a.color) || "default",
71
+ theme: ((_b = this.config.theme) == null ? void 0 : _b.darkMode) || "light",
72
+ language: this.config.language || "fa"
73
+ });
74
+ }
99
75
  }
100
- /** توکن را از میزبان دریافت کرده و آن را ذخیره می‌کند. سپس به میزبان اعلام می‌کند که آماده‌ی ادامه‌ی کار است */
101
- setToken(data) {
102
- setToken(data.data.token);
103
- clearFrame();
104
- window.dispatchEvent(getReadyEvent());
76
+ handleOpenLink(data) {
77
+ if (this.config.linkHandler) {
78
+ this.config.linkHandler(data.link);
79
+ } else {
80
+ console.warn("[SamanDesk] openLink requested but no linkHandler configured.");
81
+ }
82
+ }
83
+ handleSetGuestReadyEvent() {
84
+ this.isReady = true;
85
+ if (this.config.onGuestReady) {
86
+ this.config.onGuestReady();
87
+ }
88
+ }
89
+ handleCloseFromInside() {
90
+ this.close();
105
91
  }
106
- /** بستن کتابخانه */
92
+ // To be implemented by adapters
93
+ sendMessage(message) {
94
+ throw new Error("sendMessage not implemented");
95
+ }
96
+ // To be implemented by adapters
107
97
  close() {
108
- clearFrame();
109
- const backdrop2 = getBackdrop();
110
- document.body.removeChild(backdrop2);
111
- backdrop2.onclick = null;
112
- document.body.removeChild(getFrame());
113
- window.dispatchEvent(getCloseEvent());
114
- }
115
- /** اطلاعات کتابخانه را به بیرون اطلاع می‌دهد */
116
- getConfig(data) {
117
- const themeData = getTheme();
118
- const languageData = getLanguage();
119
- resolveRun(data.id, { color: themeData.color, theme: themeData.theme, language: languageData.language }, data.action);
120
- }
121
- /** لینک موردنظر را به وسیله‌ی تابع از پیش تعریف‌شده باز می‌کند */
122
- openLink(data) {
123
- getOpenLinkFn()(data.data.link);
124
- }
125
- setGuestReadyEvent() {
126
- window.dispatchEvent(setGuestReadyEvent());
98
+ if (this.config.onClose) {
99
+ this.config.onClose();
100
+ }
127
101
  }
128
- }
129
- const SITE_URL = "https://samandesk.com";
130
- class SamanDesk {
131
- /**
132
- * ⁧این تابع در ابتدا مقدار verifiedToken را دریافت کرده و سپس مقدار token‌ را دریافت و سپس آن را ذخیره می‌کند
133
- */
134
- async init(config, verifiedToken) {
135
- this.interoperation = new InterOperation();
136
- this.interoperation.init();
137
- const frame2 = getFrame();
138
- const backdrop2 = getBackdrop();
139
- setTheme({
140
- darkMode: config.theme.darkMode ?? "light",
141
- color: config.theme.color ?? "default"
102
+ // To be implemented by adapters
103
+ open(link) {
104
+ if (this.config.onOpen) {
105
+ this.config.onOpen();
106
+ }
107
+ }
108
+ // To be implemented by adapters
109
+ destroy() {
110
+ }
111
+ post(action, data = {}) {
112
+ this.sendMessage({ from: "SamanDesk", action, data });
113
+ }
114
+ run(action, data = {}) {
115
+ const id = ++this.promiseCount;
116
+ this.sendMessage({ from: "SamanDesk", action, data, id });
117
+ return new Promise((resolve) => {
118
+ this.runPromises[id] = resolve;
142
119
  });
143
- setLanguage(config.language ?? "fa");
144
- setOpenLinkFn(config.linkHandler);
145
- this.config = config;
146
- if (verifiedToken) {
147
- setFrameSrc(`${SITE_URL}/setup-interoperation/${verifiedToken}`);
148
- frame2.width = "0";
149
- frame2.style.width = "0";
150
- } else {
151
- this.setStyle();
152
- setFrameSrc(`${SITE_URL}/guest-login/${config.workspaceID}`);
153
- }
154
- document.body.appendChild(backdrop2);
155
- document.body.appendChild(frame2);
156
- }
157
- /**
158
- * استایل مربوط به آیفریم را تنظیم می‌کند
159
- *
160
- * @param fullScreen - تمام صفحه باز شود یا خیر
161
- *
162
- */
163
- setStyle() {
120
+ }
121
+ sendResponse(id, action, data) {
122
+ this.sendMessage({ from: "SamanDesk", action, data, id });
123
+ }
124
+ }
125
+ function createSamanDeskWeb(config, options = {}) {
126
+ const frame = document.createElement("iframe");
127
+ const backdrop = document.createElement("div");
128
+ let isFrameAttached = false;
129
+ const core = new SamanDeskCore(config);
130
+ const applyStyles = () => {
164
131
  var _a, _b, _c, _d;
165
- const frame2 = getFrame();
166
- const backdrop2 = getBackdrop();
167
- frame2.style.position = "fixed";
168
- frame2.style.zIndex = "1000";
169
- const backdropStyles = {
132
+ Object.assign(backdrop.style, {
170
133
  position: "fixed",
171
134
  top: "0",
172
135
  bottom: "0",
173
136
  left: "0",
174
137
  right: "0",
175
138
  backgroundColor: "rgba(0,0,0,0.5)",
176
- zIndex: "1000"
177
- };
178
- Object.assign(backdrop2.style, backdropStyles);
179
- if (this.config.fullScreen) {
180
- const marginTopVal = ((_a = this.config.style) == null ? void 0 : _a.marginTop) ?? 0;
181
- const marginBottomVal = ((_b = this.config.style) == null ? void 0 : _b.marginBottom) ?? 0;
139
+ zIndex: "1000",
140
+ display: "none"
141
+ });
142
+ Object.assign(frame.style, {
143
+ position: "fixed",
144
+ zIndex: "1000",
145
+ border: "none",
146
+ display: "none"
147
+ });
148
+ if (options.fullScreen) {
149
+ const marginTopVal = ((_a = options.style) == null ? void 0 : _a.marginTop) ?? 0;
150
+ const marginBottomVal = ((_b = options.style) == null ? void 0 : _b.marginBottom) ?? 0;
182
151
  const totalMargin = marginTopVal + marginBottomVal;
183
- Object.assign(frame2.style, {
184
- bottom: ((_c = this.config.style) == null ? void 0 : _c.marginBottom) ? `${this.config.style.marginBottom.toString()}px` : "0",
152
+ Object.assign(frame.style, {
153
+ bottom: `${marginBottomVal}px`,
185
154
  left: "0",
186
- top: ((_d = this.config.style) == null ? void 0 : _d.marginTop) ? `${this.config.style.marginTop.toString()}px` : "0",
155
+ top: `${marginTopVal}px`,
187
156
  right: "0",
188
157
  width: "100%",
189
158
  height: `calc(100% - ${totalMargin}px)`
190
159
  });
191
160
  } else {
192
- Object.assign(frame2.style, {
161
+ Object.assign(frame.style, {
193
162
  top: "50%",
194
163
  left: "50%",
195
164
  transform: "translate(-50%, -50%)",
196
- width: "100%",
165
+ width: ((_c = options.style) == null ? void 0 : _c.width) || "100%",
197
166
  maxWidth: "500px",
198
167
  boxShadow: "0px 0px 100px 0px rgba(0,0,0,0.48)",
199
168
  borderRadius: "24px",
200
- height: "100%",
169
+ height: ((_d = options.style) == null ? void 0 : _d.height) || "100%",
201
170
  maxHeight: "660px"
202
171
  });
203
172
  }
204
- document.body.appendChild(backdrop2);
205
- document.body.appendChild(frame2);
206
- backdrop2.onclick = () => {
207
- this.close();
208
- };
173
+ };
174
+ const attachFrame = () => {
175
+ if (!isFrameAttached) {
176
+ applyStyles();
177
+ document.body.appendChild(backdrop);
178
+ document.body.appendChild(frame);
179
+ backdrop.onclick = () => webApi.close();
180
+ window.addEventListener("message", handleMessage);
181
+ isFrameAttached = true;
182
+ }
183
+ };
184
+ const detachFrame = () => {
185
+ if (isFrameAttached) {
186
+ document.body.removeChild(backdrop);
187
+ document.body.removeChild(frame);
188
+ window.removeEventListener("message", handleMessage);
189
+ isFrameAttached = false;
190
+ }
191
+ };
192
+ const show = (src) => {
193
+ attachFrame();
194
+ frame.src = src;
195
+ frame.style.display = "block";
196
+ if (!options.fullScreen) {
197
+ backdrop.style.display = "block";
198
+ }
199
+ if (config.onOpen) config.onOpen();
200
+ };
201
+ const hide = () => {
202
+ frame.src = "about:blank";
203
+ frame.style.display = "none";
204
+ backdrop.style.display = "none";
205
+ if (config.onClose) config.onClose();
206
+ };
207
+ const handleMessage = (event) => {
208
+ core.handleIncomingMessage(event.data);
209
+ };
210
+ core.sendMessage = (message) => {
211
+ if (frame.contentWindow) {
212
+ frame.contentWindow.postMessage(message, config.siteUrl || "*");
213
+ }
214
+ };
215
+ const webApi = {
216
+ open: (link) => {
217
+ show(core.getOpenUrl(link));
218
+ },
219
+ close: () => {
220
+ hide();
221
+ },
222
+ destroy: () => {
223
+ hide();
224
+ detachFrame();
225
+ }
226
+ };
227
+ const initialSrc = core.getInitialUrl();
228
+ frame.src = initialSrc;
229
+ if (!config.verifiedToken) {
230
+ show(initialSrc);
231
+ } else {
232
+ attachFrame();
233
+ frame.style.width = "0";
234
+ frame.style.height = "0";
235
+ frame.style.display = "block";
209
236
  }
210
- /**
211
- * این تابع آیفریم را باز می‌کند.
212
- */
213
- open(link) {
214
- const src = link || `${SITE_URL}/department-selection?token=${getToken()}`;
215
- this.setStyle();
216
- setFrameSrc(src);
217
- window.dispatchEvent(getOpenEvent());
218
- }
219
- /**
220
- * بستن کتابخانه
221
- */
222
- close() {
223
- var _a;
224
- (_a = this.interoperation) == null ? void 0 : _a.close();
237
+ return webApi;
238
+ }
239
+ function createSamanDeskReactNative(config, options) {
240
+ class SamanDeskCoreReactNative extends SamanDeskCore {
241
+ sendMessage(message) {
242
+ const script = `
243
+ window.dispatchEvent(new MessageEvent('message', {
244
+ data: ${JSON.stringify(message)}
245
+ }));
246
+ `;
247
+ options.injectJavaScript(script);
248
+ }
225
249
  }
250
+ const core = new SamanDeskCoreReactNative(config);
251
+ const updateState = (updater) => {
252
+ const newState = {
253
+ source: { uri: "" },
254
+ visible: false,
255
+ ...updater(currentState)
256
+ };
257
+ currentState = newState;
258
+ options.onStateChange(currentState);
259
+ };
260
+ const initialUrl = core.getInitialUrl();
261
+ let currentState = {
262
+ source: { uri: initialUrl },
263
+ visible: !config.verifiedToken
264
+ };
265
+ const rnApi = {
266
+ getState: () => {
267
+ return currentState;
268
+ },
269
+ open: (link) => {
270
+ updateState((prev) => ({
271
+ ...prev,
272
+ source: { uri: core.getOpenUrl(link) },
273
+ visible: true
274
+ }));
275
+ if (config.onOpen) config.onOpen();
276
+ },
277
+ close: () => {
278
+ updateState((prev) => ({
279
+ ...prev,
280
+ visible: false,
281
+ source: { uri: "about:blank" }
282
+ }));
283
+ if (config.onClose) config.onClose();
284
+ },
285
+ destroy: () => {
286
+ rnApi.close();
287
+ },
288
+ getWebViewProps: () => ({
289
+ // source: currentState.source,
290
+ onMessage: (event) => {
291
+ try {
292
+ core.handleIncomingMessage(JSON.parse(event.nativeEvent.data));
293
+ } catch (error) {
294
+ console.error(
295
+ "[SamanDesk-RN] Error parsing incoming message:",
296
+ error
297
+ );
298
+ }
299
+ }
300
+ // onLoadEnd: () => {
301
+ // // Potentially useful for signaling when the webview has loaded initial content
302
+ // },
303
+ // javaScriptEnabled: true,
304
+ // originWhitelist: ["*"], // Be more specific in production
305
+ })
306
+ };
307
+ return rnApi;
226
308
  }
227
309
  export {
228
- SamanDesk
310
+ createSamanDeskReactNative,
311
+ createSamanDeskWeb
229
312
  };
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).SamanDesk={})}(this,(function(e){"use strict";let t;const o=document.createElement("iframe");let n,i,s,a=e=>window.open(e,"_blank","noopener");const d=document.createElement("div"),l={count:0,promises:{}};function r(){o.src=""}function c(e){o.src=e}function p(){return o}function m(){return d}const g=new Event("ready"),h=new Event("open"),f=new Event("close"),u=new Event("guest-ready");function y(e){window.addEventListener("message",(t=>{if(!t.data.from||"SamanDesk"!==t.data.from)return;let o=!1;console.log(`message from (${t.origin})`,t);try{e[t.data.action](t.data),o=!0}catch(a){console.error("error while run event action")}const n=t.data,i=n.id,s=l;i in s.promises&&s.promises[i]?(s.promises[i](n),delete s.promises[i]):o||console.error("Message from outside of frame is invalid")}))}class v{async init(){y(this)}setToken(e){var o;o=e.data.token,t=o,r(),window.dispatchEvent(g)}close(){r();const e=m();document.body.removeChild(e),e.onclick=null,document.body.removeChild(p()),window.dispatchEvent(f)}getConfig(e){const t={theme:n,color:i},o={language:s};!function(e,t,o){var n;const i={from:"SamanDesk",action:o,data:t,id:e};null==(n=p().contentWindow)||n.postMessage(i,"*")}(e.id,{color:t.color,theme:t.theme,language:o.language},e.action)}openLink(e){a(e.data.link)}setGuestReadyEvent(){window.dispatchEvent(u)}}const w="https://samandesk.com";e.SamanDesk=class{async init(e,t){this.interoperation=new v,this.interoperation.init();const o=p(),d=m();var l,r,g;l={darkMode:e.theme.darkMode??"light",color:e.theme.color??"default"},n=l.darkMode,i=l.color,r=e.language??"fa",s=r,(g=e.linkHandler)&&(a=g),this.config=e,t?(c(`${w}/setup-interoperation/${t}`),o.width="0",o.style.width="0"):(this.setStyle(),c(`${w}/guest-login/${e.workspaceID}`)),document.body.appendChild(d),document.body.appendChild(o)}setStyle(){var e,t,o,n;const i=p(),s=m();i.style.position="fixed",i.style.zIndex="1000";if(Object.assign(s.style,{position:"fixed",top:"0",bottom:"0",left:"0",right:"0",backgroundColor:"rgba(0,0,0,0.5)",zIndex:"1000"}),this.config.fullScreen){const s=((null==(e=this.config.style)?void 0:e.marginTop)??0)+((null==(t=this.config.style)?void 0:t.marginBottom)??0);Object.assign(i.style,{bottom:(null==(o=this.config.style)?void 0:o.marginBottom)?`${this.config.style.marginBottom.toString()}px`:"0",left:"0",top:(null==(n=this.config.style)?void 0:n.marginTop)?`${this.config.style.marginTop.toString()}px`:"0",right:"0",width:"100%",height:`calc(100% - ${s}px)`})}else Object.assign(i.style,{top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:"100%",maxWidth:"500px",boxShadow:"0px 0px 100px 0px rgba(0,0,0,0.48)",borderRadius:"24px",height:"100%",maxHeight:"660px"});document.body.appendChild(s),document.body.appendChild(i),s.onclick=()=>{this.close()}}open(e){const o=e||`${w}/department-selection?token=${t}`;this.setStyle(),c(o),window.dispatchEvent(h)}close(){var e;null==(e=this.interoperation)||e.close()}},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
1
+ !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).SamanDesk={})}(this,(function(e){"use strict";const n={};class t{constructor(e){this.runPromises={},this.promiseCount=0,this.isReady=!1,this.config={siteUrl:this.defaultSiteUrl,language:"fa",theme:{darkMode:"light",color:"default"},...e}}get defaultSiteUrl(){return"1"===(null==n?void 0:n.VITE_DEV_MODE)?"http://localhost:2525":"https://samandesk.com"}getInitialUrl(){return this.config.verifiedToken?`${this.config.siteUrl}/setup-interoperation/${this.config.verifiedToken}`:`${this.config.siteUrl}/guest-login/${this.config.workspaceID}`}getOpenUrl(e){return e||`${this.config.siteUrl}/department-selection?token=${this.token}`}handleIncomingMessage(e){if(!e||"SamanDesk"!==e.from)return;const{action:n,id:t,data:s}=e;if(void 0!==t&&this.runPromises[t])return this.runPromises[t](s),void delete this.runPromises[t];switch(n){case"setToken":this.handleSetToken(s);break;case"getConfig":this.handleGetConfig(t);break;case"openLink":this.handleOpenLink(s);break;case"setGuestReadyEvent":this.handleSetGuestReadyEvent();break;case"close":this.handleCloseFromInside();break;default:console.warn(`[SamanDesk] Unhandled action: ${n}`)}}handleSetToken(e){e&&e.token&&(this.token=e.token,this.isReady=!0,this.config.onReady&&this.config.onReady(this.token),this.config.onTokenChange&&this.config.onTokenChange(this.token))}handleGetConfig(e){var n,t;void 0!==e&&this.sendResponse(e,"getConfig",{color:(null==(n=this.config.theme)?void 0:n.color)||"default",theme:(null==(t=this.config.theme)?void 0:t.darkMode)||"light",language:this.config.language||"fa"})}handleOpenLink(e){this.config.linkHandler?this.config.linkHandler(e.link):console.warn("[SamanDesk] openLink requested but no linkHandler configured.")}handleSetGuestReadyEvent(){this.isReady=!0,this.config.onGuestReady&&this.config.onGuestReady()}handleCloseFromInside(){this.close()}sendMessage(e){throw new Error("sendMessage not implemented")}close(){this.config.onClose&&this.config.onClose()}open(e){this.config.onOpen&&this.config.onOpen()}destroy(){}post(e,n={}){this.sendMessage({from:"SamanDesk",action:e,data:n})}run(e,n={}){const t=++this.promiseCount;return this.sendMessage({from:"SamanDesk",action:e,data:n,id:t}),new Promise((e=>{this.runPromises[t]=e}))}sendResponse(e,n,t){this.sendMessage({from:"SamanDesk",action:n,data:t,id:e})}}e.createSamanDeskReactNative=function(e,n){const s=new class extends t{sendMessage(e){const t=`\n window.dispatchEvent(new MessageEvent('message', {\n data: ${JSON.stringify(e)}\n }));\n `;n.injectJavaScript(t)}}(e),o=e=>{const t={source:{uri:""},visible:!1,...e(i)};i=t,n.onStateChange(i)};let i={source:{uri:s.getInitialUrl()},visible:!e.verifiedToken};const a={getState:()=>i,open:n=>{o((e=>({...e,source:{uri:s.getOpenUrl(n)},visible:!0}))),e.onOpen&&e.onOpen()},close:()=>{o((e=>({...e,visible:!1,source:{uri:"about:blank"}}))),e.onClose&&e.onClose()},destroy:()=>{a.close()},getWebViewProps:()=>({onMessage:e=>{try{s.handleIncomingMessage(JSON.parse(e.nativeEvent.data))}catch(n){console.error("[SamanDesk-RN] Error parsing incoming message:",n)}}})};return a},e.createSamanDeskWeb=function(e,n={}){const s=document.createElement("iframe"),o=document.createElement("div");let i=!1;const a=new t(e),l=()=>{i||((()=>{var e,t,i,a;if(Object.assign(o.style,{position:"fixed",top:"0",bottom:"0",left:"0",right:"0",backgroundColor:"rgba(0,0,0,0.5)",zIndex:"1000",display:"none"}),Object.assign(s.style,{position:"fixed",zIndex:"1000",border:"none",display:"none"}),n.fullScreen){const o=(null==(e=n.style)?void 0:e.marginTop)??0,i=(null==(t=n.style)?void 0:t.marginBottom)??0,a=o+i;Object.assign(s.style,{bottom:`${i}px`,left:"0",top:`${o}px`,right:"0",width:"100%",height:`calc(100% - ${a}px)`})}else Object.assign(s.style,{top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:(null==(i=n.style)?void 0:i.width)||"100%",maxWidth:"500px",boxShadow:"0px 0px 100px 0px rgba(0,0,0,0.48)",borderRadius:"24px",height:(null==(a=n.style)?void 0:a.height)||"100%",maxHeight:"660px"})})(),document.body.appendChild(o),document.body.appendChild(s),o.onclick=()=>h.close(),window.addEventListener("message",c),i=!0)},r=t=>{l(),s.src=t,s.style.display="block",n.fullScreen||(o.style.display="block"),e.onOpen&&e.onOpen()},d=()=>{s.src="about:blank",s.style.display="none",o.style.display="none",e.onClose&&e.onClose()},c=e=>{a.handleIncomingMessage(e.data)};a.sendMessage=n=>{s.contentWindow&&s.contentWindow.postMessage(n,e.siteUrl||"*")};const h={open:e=>{r(a.getOpenUrl(e))},close:()=>{d()},destroy:()=>{d(),i&&(document.body.removeChild(o),document.body.removeChild(s),window.removeEventListener("message",c),i=!1)}},g=a.getInitialUrl();return s.src=g,e.verifiedToken?(l(),s.style.width="0",s.style.height="0",s.style.display="block"):r(g),h},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,30 @@
1
+ import { SamanDeskConfig } from '../../core/types';
2
+ interface ReactNativeState {
3
+ source: {
4
+ uri: string;
5
+ };
6
+ visible: boolean;
7
+ }
8
+ interface ReactNativeOptions {
9
+ onStateChange: (state: ReactNativeState) => void;
10
+ injectJavaScript: (script: string) => void;
11
+ }
12
+ export declare function createSamanDeskReactNative(config: SamanDeskConfig, options: ReactNativeOptions): {
13
+ getState: () => {
14
+ source: {
15
+ uri: string;
16
+ };
17
+ visible: boolean;
18
+ };
19
+ open: (link?: string) => void;
20
+ close: () => void;
21
+ destroy: () => void;
22
+ getWebViewProps: () => {
23
+ onMessage: (event: {
24
+ nativeEvent: {
25
+ data: string;
26
+ };
27
+ }) => void;
28
+ };
29
+ };
30
+ export {};
@@ -0,0 +1,16 @@
1
+ import { SamanDeskConfig } from '../../core/types';
2
+ interface WebOptions {
3
+ fullScreen?: boolean;
4
+ style?: {
5
+ marginTop?: number;
6
+ marginBottom?: number;
7
+ width?: string;
8
+ height?: string;
9
+ };
10
+ }
11
+ export declare function createSamanDeskWeb(config: SamanDeskConfig, options?: WebOptions): {
12
+ open: (link?: string) => void;
13
+ close: () => void;
14
+ destroy: () => void;
15
+ };
16
+ export {};
@@ -0,0 +1,29 @@
1
+ import { SamanDeskConfig, OutgoingMessage } from './types';
2
+ export declare class SamanDeskCore {
3
+ config: SamanDeskConfig;
4
+ protected token?: string;
5
+ protected runPromises: Record<number, (data: any) => void>;
6
+ protected promiseCount: number;
7
+ isReady: boolean;
8
+ protected get defaultSiteUrl(): "http://localhost:2525" | "https://samandesk.com";
9
+ constructor(config: SamanDeskConfig);
10
+ getInitialUrl(): string;
11
+ getOpenUrl(link?: string): string;
12
+ handleIncomingMessage(message: any): void;
13
+ protected handleSetToken(data: {
14
+ token?: string;
15
+ }): void;
16
+ protected handleGetConfig(id?: number): void;
17
+ protected handleOpenLink(data: {
18
+ link: string;
19
+ }): void;
20
+ protected handleSetGuestReadyEvent(): void;
21
+ protected handleCloseFromInside(): void;
22
+ protected sendMessage(message: OutgoingMessage): void;
23
+ close(): void;
24
+ open(link?: string): void;
25
+ destroy(): void;
26
+ protected post(action: string, data?: object): void;
27
+ protected run<T>(action: string, data?: object): Promise<T>;
28
+ protected sendResponse(id: number, action: string, data: any): void;
29
+ }
@@ -0,0 +1,50 @@
1
+ import { Color } from '../../library-colors';
2
+ export type ProjectTheme = {
3
+ /** روشن یا تیره‌بودن تم برنامه */
4
+ darkMode: 'light' | 'dark';
5
+ /** رنگ برنامه */
6
+ color: Color;
7
+ };
8
+ export interface SamanDeskConfig {
9
+ /** شناسه محیط کاری پروژه */
10
+ workspaceID?: string;
11
+ /** توکن وریفای شده */
12
+ verifiedToken?: string;
13
+ /** تم کتابخانه شامل :
14
+ * روشن یا تیره بودن کتابخانه
15
+ * رنگ تم کتابخانه
16
+ */
17
+ theme?: Partial<ProjectTheme>;
18
+ /** زبان کتابخانه */
19
+ language?: 'fa' | 'en' | 'ar' | string;
20
+ /** آدرس سرور */
21
+ siteUrl?: string;
22
+ /** تابع مدیریت‌کننده‌ی لینک‌ها */
23
+ linkHandler?: (link: string) => void;
24
+ /** زمانی که کتابخانه آماده شد */
25
+ onReady?: (token: string) => void;
26
+ /** وقتی که توکن عوض میشه */
27
+ onTokenChange?: (token: string) => void;
28
+ /** باز شدن کتابخانه */
29
+ onOpen?: () => void;
30
+ /** بسته شدن کتابخانه */
31
+ onClose?: () => void;
32
+ /** آماده شدن گست */
33
+ onGuestReady?: () => void;
34
+ /** هندل کردن ارور ها */
35
+ onError?: (error: Error) => void;
36
+ }
37
+ export type MessageData<T = {}> = {
38
+ /** شناسه داده */
39
+ id: number;
40
+ /** تابعی که داده برای آن ارسال شده است */
41
+ action: string;
42
+ /** داده موردنظر */
43
+ data: T;
44
+ };
45
+ export interface OutgoingMessage {
46
+ from: 'SamanDesk';
47
+ action: string;
48
+ data?: any;
49
+ id?: number;
50
+ }
@@ -1,53 +1,3 @@
1
- import { ProjectTheme } from './data';
2
- export interface OpenConfig {
3
- /**
4
- * کتابخانه به صورت تمام صفحه باز شود یا خیر
5
- *
6
- * اگر تمام صفحه شود، از اطراف هیچ فضای خالی‌ای وجود نخواهد داشت و در غیر اینصورت، در وسط صفحه قرار خواهدگرفت
7
- * ⁧در این حالت یک overlay پشت کتابخانه قرار خواهد گرفت
8
- */
9
- fullScreen: boolean;
10
- /** تم کتابخانه شامل :
11
- * روشن یا تیره بودن کتابخانه
12
- * رنگ تم کتابخانه
13
- */
14
- theme: Partial<ProjectTheme>;
15
- /** زبان کتابیخانه */
16
- language: string;
17
- /** تابع مدیریت‌کننده‌ی لینک‌ها */
18
- linkHandler?: (link: string) => void;
19
- /** شناسه محیط کاری پروژه */
20
- workspaceID?: string;
21
- style?: {
22
- /** فاصله از بالا در حالت تمام صفحه */
23
- marginTop?: number;
24
- /** فاصله از پایین در حالت تمام صفحه */
25
- marginBottom?: number;
26
- };
27
- }
28
- export declare class SamanDesk {
29
- /**
30
- * ⁧ شی‌ء کلاس InterOperation
31
- */
32
- private interoperation?;
33
- private config;
34
- /**
35
- * ⁧این تابع در ابتدا مقدار verifiedToken را دریافت کرده و سپس مقدار token‌ را دریافت و سپس آن را ذخیره می‌کند
36
- */
37
- init(config: OpenConfig, verifiedToken?: string): Promise<void>;
38
- /**
39
- * استایل مربوط به آیفریم را تنظیم می‌کند
40
- *
41
- * @param fullScreen - تمام صفحه باز شود یا خیر
42
- *
43
- */
44
- private setStyle;
45
- /**
46
- * این تابع آیفریم را باز می‌کند.
47
- */
48
- open(link?: string): void;
49
- /**
50
- * بستن کتابخانه
51
- */
52
- close(): void;
53
- }
1
+ export * from './core/types';
2
+ export { createSamanDeskWeb } from './adapters/web';
3
+ export { createSamanDeskReactNative } from './adapters/react-native';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samandesk",
3
- "version": "1.2.0",
3
+ "version": "2.0.0-beta2.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,19 +11,20 @@
11
11
  "types": "./dist/src/index.d.ts",
12
12
  "exports": {
13
13
  ".": {
14
+ "types": "./dist/src/index.d.ts",
14
15
  "import": "./dist/saman-desk.js",
15
- "require": "./dist/saman-desk.umd.cjs",
16
- "types": "./dist/src/index.d.ts"
16
+ "require": "./dist/saman-desk.umd.cjs"
17
17
  }
18
18
  },
19
19
  "scripts": {
20
20
  "start": "yarn link && cross-env VITE_DEV_MODE=1 yarn vite build --watch",
21
- "prepublish": "vite build"
21
+ "prepublish": "cross-env VITE_DEV_MODE=0 vite build"
22
22
  },
23
23
  "devDependencies": {
24
24
  "cross-env": "^7.0.3",
25
25
  "terser": "^5.39.0",
26
26
  "typescript": "^5.8.3",
27
+ "uncrypto": "^0.1.3",
27
28
  "utility-types": "^3.11.0",
28
29
  "vite": "^6.3.3",
29
30
  "vite-plugin-dts": "^4.5.3"
@@ -1,60 +0,0 @@
1
- import { Color } from '../library-colors';
2
- export type ProjectTheme = {
3
- /** روشن یا تیره‌بودن تم برنامه */
4
- darkMode: 'light' | 'dark';
5
- /** رنگ برنامه */
6
- color: Color;
7
- };
8
- export type GuestData = {
9
- mobile: string;
10
- name?: string;
11
- family: string;
12
- countryCode: string;
13
- verifyToken: string;
14
- workspaceID: string;
15
- };
16
- type PromiseMap = {
17
- [key: number]: (data: any) => void;
18
- };
19
- /** آبجکت پرامیس‌های منتظر اجرا */
20
- export declare function getRun(): {
21
- count: number;
22
- promises: PromiseMap;
23
- };
24
- /** تنظیم تعداد پرامیس‌های منتظر اجرا */
25
- export declare function setRunCount(count: number): void;
26
- /** تنظیم پرامیس‌های منتظر اجرا */
27
- export declare function setRunPromises(promises: PromiseMap): void;
28
- /** تنظیم کردن توکن */
29
- export declare function setToken(newToken: string): void;
30
- /** توکن را بر می‌گرداند */
31
- export declare function getToken(): string | undefined;
32
- /** ⁧تگ iframe را حذف می‌کند ⁩ */
33
- export declare function clearFrame(): void;
34
- /** ⁧مقدار src تگ iframe‌ را براساس ورودی تنظیم می‌کند */
35
- export declare function setFrameSrc(src: string): void;
36
- /** ⁧تگ iframe را بر می‌گرداند ⁩ */
37
- export declare function getFrame(): HTMLIFrameElement;
38
- export declare function setGuestData(data: GuestData): void;
39
- export declare function getGuestData(): GuestData | undefined;
40
- export declare function clearGuestData(): void;
41
- /** تم و رنگ کتابخانه را بر می‌گرداند */
42
- export declare function getTheme(): {
43
- theme: "light" | "dark" | undefined;
44
- color: Color;
45
- };
46
- /** زبان کتابخانه را بر می‌گرداند */
47
- export declare function getLanguage(): {
48
- language: string;
49
- };
50
- /** ‌ تم و رنگ جدید کتابخانه را تنظیم می‌کند */
51
- export declare function setTheme(newTheme: ProjectTheme): void;
52
- /** زبان جدید کتابخانه را تنظیم می‌کند */
53
- export declare function setLanguage(newLanguage: string): void;
54
- /** عنصر بکدراپ را برمی‌گرداند */
55
- export declare function getBackdrop(): HTMLDivElement;
56
- /** تابع مدیریت‌کننده‌ی لینک‌ها را ست می‌کند */
57
- export declare function setOpenLinkFn(fn?: (link: string) => void): void;
58
- /** تابع مدیریت‌کننده‌ی لینک‌ها را برمی‌گرداند */
59
- export declare function getOpenLinkFn(): (link: string) => void;
60
- export {};
@@ -1,21 +0,0 @@
1
- import { InterOperation } from './interoperation';
2
- /**
3
- * ایونت آماده‌شدن توکن را بر می‌گرداند
4
- */
5
- export declare function getReadyEvent(): Event;
6
- /**
7
- * ایونت بازشدن کتابخانه را بر می‌گرداند
8
- */
9
- export declare function getOpenEvent(): Event;
10
- /**
11
- * ایونت بسته‌شدن کتابخانه را بر می‌گرداند
12
- */
13
- export declare function getCloseEvent(): Event;
14
- /**
15
- * ایونت آماده‌شدن حالت مهمان را بر می‌گرداند
16
- */
17
- export declare function setGuestReadyEvent(): Event;
18
- /**
19
- * ⁧eventهایی که از بیرون به داخل می‌ایند را اعتبار سنجی و سپس کار خواسته شده را انجام می‌دهد
20
- */
21
- export declare function handleEvents(interoperation: InterOperation): void;
@@ -1,34 +0,0 @@
1
- import { FunctionKeys } from 'utility-types';
2
- type MessageData<T = {}> = {
3
- /** شناسه داده */
4
- id: number;
5
- /** تابعی که داده برای آن ارسال شده است */
6
- action: string;
7
- /** داده موردنظر */
8
- data: T;
9
- };
10
- /**
11
- * این تابع برای فراخوانی متدهاییست که مقداری بر نمی‌گردانند
12
- */
13
- export declare function post(action: string, data?: object): void;
14
- /**
15
- * این تابع برای فراخوانی متدهاییست که مقدار بر می‌گردانند
16
- */
17
- export declare function run<A extends FunctionKeys<InterOperation>>(action: A, data?: object): Promise<ReturnType<InterOperation[A]>>;
18
- export declare class InterOperation {
19
- init(): Promise<void>;
20
- /** توکن را از میزبان دریافت کرده و آن را ذخیره می‌کند. سپس به میزبان اعلام می‌کند که آماده‌ی ادامه‌ی کار است */
21
- setToken(data: MessageData<{
22
- token: string;
23
- }>): void;
24
- /** بستن کتابخانه */
25
- close(): void;
26
- /** اطلاعات کتابخانه را به بیرون اطلاع می‌دهد */
27
- getConfig(data: MessageData): void;
28
- /** لینک موردنظر را به وسیله‌ی تابع از پیش تعریف‌شده باز می‌کند */
29
- openLink(data: MessageData<{
30
- link: string;
31
- }>): void;
32
- setGuestReadyEvent(): void;
33
- }
34
- export {};