@dittolive/ditto-chat-core 0.0.1 → 0.1.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/README.md +377 -0
- package/dist/index.es.js +1764 -0
- package/dist/index.umd.js +5 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/slices/useChatUser.d.ts +22 -0
- package/dist/src/slices/useMessages.d.ts +41 -0
- package/dist/src/slices/useRBAC.d.ts +8 -0
- package/dist/src/slices/useRooms.d.ts +31 -0
- package/dist/src/types/ChatUser.d.ts +11 -0
- package/dist/src/types/Message.d.ts +30 -0
- package/dist/src/types/MessageWithUser.d.ts +9 -0
- package/dist/src/types/RBAC.d.ts +11 -0
- package/dist/src/types/Room.d.ts +13 -0
- package/dist/src/useChat.d.ts +37 -0
- package/dist/tests/setup.d.ts +14 -0
- package/dist/tests/useChat.edge-cases.test.d.ts +1 -0
- package/dist/tests/useChat.error.test.d.ts +1 -0
- package/dist/tests/useChat.test.d.ts +1 -0
- package/dist/tests/useChatUser.test.d.ts +1 -0
- package/dist/tests/useMessages.test.d.ts +1 -0
- package/dist/tests/useRBAC.test.d.ts +1 -0
- package/dist/tests/useRooms.test.d.ts +1 -0
- package/package.json +66 -16
- package/src/index.ts +9 -0
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -14
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
(function(E,A){typeof exports=="object"&&typeof module<"u"?A(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],A):(E=typeof globalThis<"u"?globalThis:E||self,A(E.DittoChatCore={},E.React))})(this,(function(E,A){"use strict";const ye={canCreateRoom:!0,canEditOwnMessage:!0,canDeleteOwnMessage:!0,canAddReaction:!0,canRemoveOwnReaction:!0,canMentionUsers:!0,canSubscribeToRoom:!0},ge=(e,t,{rbacConfig:r={}})=>({rbacConfig:r,canPerformAction:s=>{const c=t().rbacConfig[s];return c!==void 0?c:ye[s]},updateRBACConfig:s=>{e(o=>({...o,rbacConfig:{...o.rbacConfig,...s}}))}});var he=Symbol.for("immer-nothing"),be=Symbol.for("immer-draftable"),O=Symbol.for("immer-state"),He=process.env.NODE_ENV!=="production"?[function(e){return`The plugin for '${e}' has not been loaded into Immer. To enable the plugin, import and call \`enable${e}()\` when initializing your application.`},function(e){return`produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${e}'`},"This object has been frozen and should not be mutated",function(e){return"Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+e},"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.","Immer forbids circular references","The first or second argument to `produce` must be a function","The third argument to `produce` must be a function or undefined","First argument to `createDraft` must be a plain object, an array, or an immerable object","First argument to `finishDraft` must be a draft returned by `createDraft`",function(e){return`'current' expects a draft, got: ${e}`},"Object.defineProperty() cannot be used on an Immer draft","Object.setPrototypeOf() cannot be used on an Immer draft","Immer only supports deleting array indices","Immer only supports setting array indices and the 'length' property",function(e){return`'original' expects a draft, got: ${e}`}]:[];function T(e,...t){if(process.env.NODE_ENV!=="production"){const r=He[e],n=typeof r=="function"?r.apply(null,t):r;throw new Error(`[Immer] ${n}`)}throw new Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var B=Object.getPrototypeOf;function k(e){return!!e&&!!e[O]}function C(e){return e?we(e)||Array.isArray(e)||!!e[be]||!!e.constructor?.[be]||F(e)||W(e):!1}var $e=Object.prototype.constructor.toString(),_e=new WeakMap;function we(e){if(!e||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);if(t===null||t===Object.prototype)return!0;const r=Object.hasOwnProperty.call(t,"constructor")&&t.constructor;if(r===Object)return!0;if(typeof r!="function")return!1;let n=_e.get(r);return n===void 0&&(n=Function.toString.call(r),_e.set(r,n)),n===$e}function $(e,t,r=!0){j(e)===0?(r?Reflect.ownKeys(e):Object.keys(e)).forEach(s=>{t(s,e[s],e)}):e.forEach((n,s)=>t(s,n,e))}function j(e){const t=e[O];return t?t.type_:Array.isArray(e)?1:F(e)?2:W(e)?3:0}function K(e,t){return j(e)===2?e.has(t):Object.prototype.hasOwnProperty.call(e,t)}function pe(e,t,r){const n=j(e);n===2?e.set(t,r):n===3?e.add(r):e[t]=r}function je(e,t){return e===t?e!==0||1/e===1/t:e!==e&&t!==t}function F(e){return e instanceof Map}function W(e){return e instanceof Set}function U(e){return e.copy_||e.base_}function ee(e,t){if(F(e))return new Map(e);if(W(e))return new Set(e);if(Array.isArray(e))return Array.prototype.slice.call(e);const r=we(e);if(t===!0||t==="class_only"&&!r){const n=Object.getOwnPropertyDescriptors(e);delete n[O];let s=Reflect.ownKeys(n);for(let o=0;o<s.length;o++){const c=s[o],a=n[c];a.writable===!1&&(a.writable=!0,a.configurable=!0),(a.get||a.set)&&(n[c]={configurable:!0,writable:!0,enumerable:a.enumerable,value:e[c]})}return Object.create(B(e),n)}else{const n=B(e);if(n!==null&&r)return{...e};const s=Object.create(n);return Object.assign(s,e)}}function te(e,t=!1){return q(e)||k(e)||!C(e)||(j(e)>1&&Object.defineProperties(e,{set:G,add:G,clear:G,delete:G}),Object.freeze(e),t&&Object.values(e).forEach(r=>te(r,!0))),e}function We(){T(2)}var G={value:We};function q(e){return e===null||typeof e!="object"?!0:Object.isFrozen(e)}var Ge={};function P(e){const t=Ge[e];return t||T(0,e),t}var z;function Se(){return z}function qe(e,t){return{drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0}}function Ee(e,t){t&&(P("Patches"),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function re(e){ne(e),e.drafts_.forEach(Qe),e.drafts_=null}function ne(e){e===z&&(z=e.parent_)}function Te(e){return z=qe(z,e)}function Qe(e){const t=e[O];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function Oe(e,t){t.unfinalizedDrafts_=t.drafts_.length;const r=t.drafts_[0];return e!==void 0&&e!==r?(r[O].modified_&&(re(t),T(4)),C(e)&&(e=Q(t,e),t.parent_||Y(t,e)),t.patches_&&P("Patches").generateReplacementPatches_(r[O].base_,e,t.patches_,t.inversePatches_)):e=Q(t,r,[]),re(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==he?e:void 0}function Q(e,t,r){if(q(t))return t;const n=e.immer_.shouldUseStrictIteration(),s=t[O];if(!s)return $(t,(o,c)=>Re(e,s,t,o,c,r),n),t;if(s.scope_!==e)return t;if(!s.modified_)return Y(e,s.base_,!0),s.base_;if(!s.finalized_){s.finalized_=!0,s.scope_.unfinalizedDrafts_--;const o=s.copy_;let c=o,a=!1;s.type_===3&&(c=new Set(o),o.clear(),a=!0),$(c,(m,y)=>Re(e,s,o,m,y,r,a),n),Y(e,o,!1),r&&e.patches_&&P("Patches").generatePatches_(s,r,e.patches_,e.inversePatches_)}return s.copy_}function Re(e,t,r,n,s,o,c){if(s==null||typeof s!="object"&&!c)return;const a=q(s);if(!(a&&!c)){if(process.env.NODE_ENV!=="production"&&s===r&&T(5),k(s)){const m=o&&t&&t.type_!==3&&!K(t.assigned_,n)?o.concat(n):void 0,y=Q(e,s,m);if(pe(r,n,y),k(y))e.canAutoFreeze_=!1;else return}else c&&r.add(s);if(C(s)&&!a){if(!e.immer_.autoFreeze_&&e.unfinalizedDrafts_<1||t&&t.base_&&t.base_[n]===s&&a)return;Q(e,s),(!t||!t.scope_.parent_)&&typeof n!="symbol"&&(F(r)?r.has(n):Object.prototype.propertyIsEnumerable.call(r,n))&&Y(e,s)}}}function Y(e,t,r=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&te(t,r)}function Ye(e,t){const r=Array.isArray(e),n={type_:r?1:0,scope_:t?t.scope_:Se(),modified_:!1,finalized_:!1,assigned_:{},parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1};let s=n,o=se;r&&(s=[n],o=L);const{revoke:c,proxy:a}=Proxy.revocable(s,o);return n.draft_=a,n.revoke_=c,a}var se={get(e,t){if(t===O)return e;const r=U(e);if(!K(r,t))return Ve(e,r,t);const n=r[t];return e.finalized_||!C(n)?n:n===oe(e.base_,t)?(ce(e),e.copy_[t]=ae(n,e)):n},has(e,t){return t in U(e)},ownKeys(e){return Reflect.ownKeys(U(e))},set(e,t,r){const n=Ie(U(e),t);if(n?.set)return n.set.call(e.draft_,r),!0;if(!e.modified_){const s=oe(U(e),t),o=s?.[O];if(o&&o.base_===r)return e.copy_[t]=r,e.assigned_[t]=!1,!0;if(je(r,s)&&(r!==void 0||K(e.base_,t)))return!0;ce(e),ie(e)}return e.copy_[t]===r&&(r!==void 0||t in e.copy_)||Number.isNaN(r)&&Number.isNaN(e.copy_[t])||(e.copy_[t]=r,e.assigned_[t]=!0),!0},deleteProperty(e,t){return oe(e.base_,t)!==void 0||t in e.base_?(e.assigned_[t]=!1,ce(e),ie(e)):delete e.assigned_[t],e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){const r=U(e),n=Reflect.getOwnPropertyDescriptor(r,t);return n&&{writable:!0,configurable:e.type_!==1||t!=="length",enumerable:n.enumerable,value:r[t]}},defineProperty(){T(11)},getPrototypeOf(e){return B(e.base_)},setPrototypeOf(){T(12)}},L={};$(se,(e,t)=>{L[e]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)}}),L.deleteProperty=function(e,t){return process.env.NODE_ENV!=="production"&&isNaN(parseInt(t))&&T(13),L.set.call(this,e,t,void 0)},L.set=function(e,t,r){return process.env.NODE_ENV!=="production"&&t!=="length"&&isNaN(parseInt(t))&&T(14),se.set.call(this,e[0],t,r,e[0])};function oe(e,t){const r=e[O];return(r?U(r):e)[t]}function Ve(e,t,r){const n=Ie(t,r);return n?"value"in n?n.value:n.get?.call(e.draft_):void 0}function Ie(e,t){if(!(t in e))return;let r=B(e);for(;r;){const n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=B(r)}}function ie(e){e.modified_||(e.modified_=!0,e.parent_&&ie(e.parent_))}function ce(e){e.copy_||(e.copy_=ee(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var Xe=class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!0,this.produce=(t,r,n)=>{if(typeof t=="function"&&typeof r!="function"){const o=r;r=t;const c=this;return function(m=o,...y){return c.produce(m,S=>r.call(this,S,...y))}}typeof r!="function"&&T(6),n!==void 0&&typeof n!="function"&&T(7);let s;if(C(t)){const o=Te(this),c=ae(t,void 0);let a=!0;try{s=r(c),a=!1}finally{a?re(o):ne(o)}return Ee(o,n),Oe(s,o)}else if(!t||typeof t!="object"){if(s=r(t),s===void 0&&(s=t),s===he&&(s=void 0),this.autoFreeze_&&te(s,!0),n){const o=[],c=[];P("Patches").generateReplacementPatches_(t,s,o,c),n(o,c)}return s}else T(1,t)},this.produceWithPatches=(t,r)=>{if(typeof t=="function")return(c,...a)=>this.produceWithPatches(c,m=>t(m,...a));let n,s;return[this.produce(t,r,(c,a)=>{n=c,s=a}),n,s]},typeof e?.autoFreeze=="boolean"&&this.setAutoFreeze(e.autoFreeze),typeof e?.useStrictShallowCopy=="boolean"&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),typeof e?.useStrictIteration=="boolean"&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){C(e)||T(8),k(e)&&(e=Ze(e));const t=Te(this),r=ae(e,void 0);return r[O].isManual_=!0,ne(t),r}finishDraft(e,t){const r=e&&e[O];(!r||!r.isManual_)&&T(9);const{scope_:n}=r;return Ee(n,t),Oe(void 0,n)}setAutoFreeze(e){this.autoFreeze_=e}setUseStrictShallowCopy(e){this.useStrictShallowCopy_=e}setUseStrictIteration(e){this.useStrictIteration_=e}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(e,t){let r;for(r=t.length-1;r>=0;r--){const s=t[r];if(s.path.length===0&&s.op==="replace"){e=s.value;break}}r>-1&&(t=t.slice(r+1));const n=P("Patches").applyPatches_;return k(e)?n(e,t):this.produce(e,s=>n(s,t))}};function ae(e,t){const r=F(e)?P("MapSet").proxyMap_(e,t):W(e)?P("MapSet").proxySet_(e,t):Ye(e,t);return(t?t.scope_:Se()).drafts_.push(r),r}function Ze(e){return k(e)||T(10,e),De(e)}function De(e){if(!C(e)||q(e))return e;const t=e[O];let r,n=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,r=ee(e,t.scope_.immer_.useStrictShallowCopy_),n=t.scope_.immer_.shouldUseStrictIteration()}else r=ee(e,!0);return $(r,(s,o)=>{pe(r,s,De(o))},n),t&&(t.finalized_=!1),r}var Je=new Xe,N=Je.produce;function bt(e){return e}const p=[];for(let e=0;e<256;++e)p.push((e+256).toString(16).slice(1));function Ke(e,t=0){return(p[e[t+0]]+p[e[t+1]]+p[e[t+2]]+p[e[t+3]]+"-"+p[e[t+4]]+p[e[t+5]]+"-"+p[e[t+6]]+p[e[t+7]]+"-"+p[e[t+8]]+p[e[t+9]]+"-"+p[e[t+10]]+p[e[t+11]]+p[e[t+12]]+p[e[t+13]]+p[e[t+14]]+p[e[t+15]]).toLowerCase()}let ue;const et=new Uint8Array(16);function tt(){if(!ue){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");ue=crypto.getRandomValues.bind(crypto)}return ue(et)}const Ae={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function rt(e,t,r){e=e||{};const n=e.random??e.rng?.()??tt();if(n.length<16)throw new Error("Random bytes length must be >= 16");return n[6]=n[6]&15|64,n[8]=n[8]&63|128,Ke(n)}function ve(e,t,r){return Ae.randomUUID&&!e?Ae.randomUUID():rt(e)}function Me(e,t,r){if(r.items.length===0)return;const n=r.items.map(s=>{const o=t().messagesPublisher;return o(s.value),s.value});e(s=>N(s,o=>{const c=s.rooms.filter(a=>a.collectionId!==n[0].collectionId);return o.rooms=c.concat(n),o.roomsLoading=!1,o}))}async function le({ditto:e,currentUserId:t,name:r,collectionId:n,messagesId:s,participants:o=[],retentionDays:c,isGenerated:a=!1,id:m}){if(e)try{const S={_id:m||ve(),name:r,messagesId:s,collectionId:n,isGenerated:a,createdBy:t,createdOn:new Date().toISOString(),participants:o||void 0,...c!==void 0&&{retentionDays:c}},R=`INSERT INTO \`${n}\` DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`;return await e.store.execute(R,{newDoc:S}),S}catch(y){console.error(`Error creating ${n}:`,y)}}const Ce=(e,t,{ditto:r,userId:n})=>{const s={rooms:[],dmRooms:[],generatedRooms:[],roomsLoading:!0,roomsObserver:null,roomsSubscription:null,dmRoomsObserver:null,dmRoomsSubscription:null,createRoom(o,c){if(!t().canPerformAction("canCreateRoom"))return console.warn("Permission denied: canCreateRoom is false"),Promise.resolve(void 0);const a=t().currentUser;return le({ditto:r,currentUserId:a?._id||n,name:o,collectionId:"rooms",messagesId:"messages",retentionDays:c?.retentionDays,isGenerated:c?.isGenerated??!1})},createDMRoom(o){const c=t().currentUser;if(!c?._id||!o?._id)throw Error("Invalid users");return le({ditto:r,currentUserId:c?._id||n,name:`${c?.name} & ${o.name}`,collectionId:"dm_rooms",messagesId:"dm_messages",participants:[c?._id,o._id]})},async createGeneratedRoom(o,c){const a=t().currentUser,m=await le({ditto:r,currentUserId:a?._id||n,name:c,collectionId:"rooms",messagesId:"messages",isGenerated:!0,id:o});return m&&e(y=>y.generatedRooms.some(R=>R._id===m._id)?y:{...y,generatedRooms:[...y.generatedRooms,m]}),m},getPublicRooms(){return t().rooms.filter(o=>!o.isGenerated)}};if(r){const o="SELECT * from rooms WHERE isGenerated = false OR isGenerated IS NULL",c="SELECT * from dm_rooms where (array_contains(participants, :userId))";s.roomsSubscription=r.sync.registerSubscription(o),s.dmRoomsSubscription=r.sync.registerSubscription(c,{userId:n}),s.roomsObserver=r.store.registerObserver(o,a=>{Me(e,t,a)}),s.dmRoomsObserver=r.store.registerObserver(c,a=>{Me(e,t,a)},{userId:n})}return s},Ue=e=>{let t;const r=new Set,n=(y,S)=>{const R=typeof y=="function"?y(t):y;if(!Object.is(R,t)){const H=t;t=S??(typeof R!="object"||R===null)?R:Object.assign({},t,R),r.forEach(X=>X(t,H))}},s=()=>t,a={setState:n,getState:s,getInitialState:()=>m,subscribe:y=>(r.add(y),()=>r.delete(y))},m=t=e(n,s,a);return a},nt=(e=>e?Ue(e):Ue),st=e=>e;function Pe(e,t=st){const r=A.useSyncExternalStore(e.subscribe,A.useCallback(()=>t(e.getState()),[e,t]),A.useCallback(()=>t(e.getInitialState()),[e,t]));return A.useDebugValue(r),r}const Ne=e=>Symbol.iterator in e,xe=e=>"entries"in e,ke=(e,t)=>{const r=e instanceof Map?e:new Map(e.entries()),n=t instanceof Map?t:new Map(t.entries());if(r.size!==n.size)return!1;for(const[s,o]of r)if(!n.has(s)||!Object.is(o,n.get(s)))return!1;return!0},ot=(e,t)=>{const r=e[Symbol.iterator](),n=t[Symbol.iterator]();let s=r.next(),o=n.next();for(;!s.done&&!o.done;){if(!Object.is(s.value,o.value))return!1;s=r.next(),o=n.next()}return!!s.done&&!!o.done};function it(e,t){return Object.is(e,t)?!0:typeof e!="object"||e===null||typeof t!="object"||t===null||Object.getPrototypeOf(e)!==Object.getPrototypeOf(t)?!1:Ne(e)&&Ne(t)?xe(e)&&xe(t)?ke(e,t):ot(e,t):ke({entries:()=>Object.entries(e)},{entries:()=>Object.entries(t)})}function ct(e){const t=A.useRef(void 0);return r=>{const n=e(r);return it(t.current,n)?t.current:t.current=n}}const at=(e,t,{ditto:r,userId:n,userCollectionKey:s})=>{const o={usersLoading:!0,currentUser:null,userObserver:null,userSubscription:null,allUsers:[],allUsersObserver:null,allUsersSubscription:null,async addUser(c){if(r)try{await r.store.execute(`INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,{newUser:c})}catch(a){console.error("Error in addUser:",a)}},async updateUser({_id:c,...a}){if(r&&c)try{const m=await t().findUserById(c);if(!m)return;const y={...m,...a};await r.store.execute(`INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,{newUser:y})}catch(m){console.error("Error in updateUser:",m)}},async findUserById(c){if(!r)return null;try{return(await r.store.execute(`SELECT * FROM ${s} WHERE _id = :id`,{id:c})).items?.[0]?.value}catch(a){return console.error("Error in findUserById:",a),null}},async markRoomAsRead(c){if(!(!r||!n))try{let a=!1;const m=await t().findUserById(n);if(!m)return;const y=m.subscriptions||{},S=m.mentions||{};if(y[c]){const R=new Date().toISOString();y[c]=R,a=!0}S[c]&&(S[c]=[],a=!0),a&&await t().updateUser({_id:n,subscriptions:y,mentions:S})}catch(a){console.error("Error in markRoomAsRead:",a)}},async toggleRoomSubscription(c){if(!(!r||!n)){if(!t().canPerformAction("canSubscribeToRoom")){console.warn("Permission denied: canSubscribeToRoom is false");return}try{const a=await t().findUserById(n);if(!a)return;const m={...a.subscriptions};if(c in m&&m[c]!==null)m[c]=null;else{const y=new Date().toISOString();m[c]=y}await t().updateUser({_id:n,subscriptions:m})}catch(a){console.error("Error in toggleRoomSubscription:",a)}}}};if(r){const c=`SELECT * FROM ${s} WHERE _id = :id`,a=`SELECT * FROM ${s}`,m={id:n};o.userSubscription=r.sync.registerSubscription(c,m),o.userObserver=r.store.registerObserver(c,y=>{e({currentUser:y.items?.[0]?.value})},m),o.allUsersSubscription=r.sync.registerSubscription(a),o.allUsersObserver=r.store.registerObserver(a,y=>{e({allUsers:y.items.map(S=>S.value),usersLoading:!1})})}return o},Be=30,ut=1e4,V=282,lt=(e,t,{ditto:r,userId:n,userCollectionKey:s,retentionDays:o,notificationHandler:c})=>{const a=async i=>{if(!r)throw new Error("Ditto not initialized");const u=await r.store.execute(`SELECT * FROM ${i.collectionId||"rooms"} WHERE _id = :id`,{id:i._id});if(u.items.length===0)throw new Error("Room not found");return u.items[0].value},m=async()=>{if(!r||!n)throw new Error("Ditto not initialized or user not found");const u=(await r.store.execute(`SELECT * FROM ${s} WHERE _id = :id`,{id:n})).items?.[0]?.value;return{id:n,name:u?.name??n}},y=()=>{e(i=>N(i,u=>{u.messagesLoading=!u.rooms.every(l=>l._id in u.messagesByRoom)}))},S=(i,u,l,f)=>{const d={message:l,user:f,id:l._id},g={...d,message:d.message,user:d.user},b=i.findIndex(h=>h.id===l._id);if(b===-1&&!l.archivedMessage&&R(l,u)&&c&&t().activeRoomId!==u._id){const _=f?.name||"Unknown User",w=u.name,v=u.collectionId==="dm_rooms"?`New message from ${_}`:`#${w}: ${_}`,M=l.text?l.text.substring(0,30)+(l.text.length>30?"...":""):"Sent an attachment";c(v,M)}if(l.archivedMessage){const h=i.findIndex(_=>_.id===l.archivedMessage);h!==-1?i[h]=g:b===-1?i.push(g):i[b]=g}else b===-1?i.push(g):i[b]=g},R=(i,u)=>{const f=t().currentUser,d=i.userId===f?._id,g=new Date(i.createdOn).getTime()>Date.now()-ut,b=f?.subscriptions?.[u._id],h=i.mentions?.some(w=>w.userId===f?._id),_=u.collectionId==="dm_rooms";return!d&&g&&(b||_||h)},H=async(i,u,l="")=>{if(!r||!n)throw new Error("Ditto not initialized or user not found");const f=await a(i),d=u._id||ve(),g={_id:d,roomId:i._id,userId:n,createdOn:new Date().toISOString(),isArchived:!1,archivedMessage:null,largeImageToken:null,thumbnailImageToken:null,fileAttachmentToken:null,isEdited:!1,isDeleted:!1,...u,mentions:u.mentions?.map(h=>({...h}))||[],reactions:u.reactions?.map(h=>({...h}))||[]},b=l?`COLLECTION ${f.messagesId} ${l}`:f.messagesId;if(await r.store.execute(`INSERT INTO ${b} DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`,{newDoc:g}),u.mentions&&u.mentions.length>0){const h=t().allUsers;await Promise.all(u.mentions.map(_=>{const w=h.find(v=>v._id===_.userId);if(!w)return;const I={...w.mentions||{},[i._id]:[...w.mentions?.[i._id]||[],d]};return r.store.execute("UPDATE users SET mentions = :mentions WHERE _id = :id",{id:w._id,mentions:I})}))}return g},X=async(i,u,l)=>{if(!r)throw new Error("Ditto not initialized");return await r.store.execute(`UPDATE ${u.messagesId} SET isArchived = :isArchived WHERE _id = :id`,{id:i._id,isArchived:!0}),H(u,{text:"",createdOn:i.createdOn,archivedMessage:i._id,...l})},Le=async(i,u,l,f)=>{if(!r||!n)throw new Error("Ditto not initialized or user not found");const d=await m(),g={thumbnailImageToken:null,largeImageToken:null,fileAttachmentToken:null};if(f==="image"){const h=await ft(u),_=await ze(h),w=new Uint8Array(await _.arrayBuffer());g.thumbnailImageToken=await r.store.newAttachment(w,fe(d.id,d.name,"thumbnail",u));const I=await ze(await Fe(u)),v=new Uint8Array(await I.arrayBuffer());g.largeImageToken=await r.store.newAttachment(v,fe(d.id,d.name,"large",u))}else{const h=new Uint8Array(await u.arrayBuffer());g.fileAttachmentToken=await r.store.newAttachment(h,fe(d.id,d.name,"file",u))}const b=f==="image"?"(thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT)":"(fileAttachmentToken ATTACHMENT)";await H(i,{text:l||(f==="file"?u.name:""),...g},b)};return{messagesLoading:!0,messagesByRoom:{},messageObserversByRoom:{},messageSubscriptionsByRoom:{},notificationHandler:null,registerNotificationHandler(i){e({notificationHandler:i})},async messagesPublisher(i,u){if(!r)return;const l=i._id;if(t().messageSubscriptionsByRoom[l])return;const f=u??i.retentionDays??o??Be,d=new Date(Date.now()-f*24*60*60*1e3),g=`SELECT * FROM COLLECTION ${i.messagesId} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
|
|
2
|
+
WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
|
|
3
|
+
ORDER BY createdOn ASC`,b={roomId:i._id,date:d.toISOString()};try{const h=r.sync.registerSubscription(g,b),_=t().allUsers,w=r.store.registerObserver(g,async I=>{e(v=>N(v,M=>{if(M.messagesByRoom[i._id]||(M.messagesByRoom[i._id]=[]),I.items.length===0)return M;for(const x of I.items){const Z=x.value,J=M.messagesByRoom[i._id],de=_.find(me=>me._id===Z.userId);S(J,i,Z,de)}return M})),y()},b);e({messageSubscriptionsByRoom:{...t().messageSubscriptionsByRoom,[l]:h},messageObserversByRoom:{...t().messageObserversByRoom,[l]:w}})}catch(h){console.error("Error in messagesPublisher:",h)}},async subscribeToRoomMessages(i,u,l){if(!r||t().messageSubscriptionsByRoom[i])return;const f=l??o??Be,d=new Date(Date.now()-f*24*60*60*1e3),g=`SELECT * FROM COLLECTION ${u} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
|
|
4
|
+
WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
|
|
5
|
+
ORDER BY createdOn ASC`,b={roomId:i,date:d.toISOString()};try{const h=r.sync.registerSubscription(g,b),_=t().allUsers,w=r.store.registerObserver(g,async I=>{const v={_id:i,name:"",messagesId:u,collectionId:"rooms",createdBy:"",createdOn:"",isGenerated:!0};e(M=>N(M,x=>{if(x.messagesByRoom[i]||(x.messagesByRoom[i]=[]),I.items.length===0)return x;for(const Z of I.items){const J=Z.value,de=x.messagesByRoom[i],me=_.find(ht=>ht._id===J.userId);S(de,v,J,me)}return x})),y()},b);e({messageSubscriptionsByRoom:{...t().messageSubscriptionsByRoom,[i]:h},messageObserversByRoom:{...t().messageObserversByRoom,[i]:w}})}catch(h){console.error("Error in subscribeToRoomMessages:",h)}},unsubscribeFromRoomMessages(i){const u=t().messageSubscriptionsByRoom[i],l=t().messageObserversByRoom[i];u&&!u.isCancelled&&u.cancel(),l&&!l.isCancelled&&l.cancel(),e(f=>N(f,d=>(delete d.messagesByRoom[i],delete d.messageSubscriptionsByRoom[i],delete d.messageObserversByRoom[i],d)))},async createMessage(i,u,l=[]){if(!r||!n)return;const f=t().canPerformAction("canMentionUsers"),d=l.length>0&&!f?[]:l;l.length>0&&!f&&console.warn("Permission denied: canMentionUsers is false");try{await H(i,{text:u,mentions:d})}catch(g){console.error("Error in createMessage:",g)}},async saveEditedTextMessage(i,u){if(!t().canPerformAction("canEditOwnMessage")){console.warn("Permission denied: canEditOwnMessage is false");return}try{await X(i,u,{text:i.text,largeImageToken:i.largeImageToken||null,thumbnailImageToken:i.thumbnailImageToken||null,fileAttachmentToken:i.fileAttachmentToken||null,isEdited:!0,isDeleted:!1,mentions:i.mentions,reactions:i.reactions})}catch(l){console.error("Error in saveEditedTextMessage:",l)}},async saveDeletedMessage(i,u,l="text"){if(!t().canPerformAction("canDeleteOwnMessage")){console.warn("Permission denied: canDeleteOwnMessage is false");return}const f={text:"[deleted message]",image:"[deleted image]",file:"[deleted file]"}[l];try{await X(i,u,{text:f,createdOn:new Date().toISOString(),largeImageToken:null,thumbnailImageToken:null,fileAttachmentToken:null,isEdited:!1,isDeleted:!0,mentions:[]})}catch(d){console.error("Error in saveDeletedMessage:",d)}},async createImageMessage(i,u,l){try{await Le(i,u,l,"image")}catch(f){console.error("Error in createImageMessage:",f)}},async createFileMessage(i,u,l){try{await Le(i,u,l,"file")}catch(f){console.error("Error in createFileMessage:",f)}},fetchAttachment(i,u,l){if(!r){l({success:!1,error:new Error("Ditto not initialized")});return}if(!i){l({success:!1,error:new Error("No attachment token provided")});return}try{r.store.fetchAttachment(i,async f=>{switch(f.type){case"Progress":{const d=Number(f.downloadedBytes)/(Number(f.totalBytes)||1);u(d);break}case"Completed":try{const d=f.attachment.getData(),g=d instanceof Promise?await d:d;l({success:!0,data:g,metadata:f.attachment.metadata||{}})}catch(d){l({success:!1,error:d instanceof Error?d:new Error("Unknown error getting attachment data")})}break;case"Deleted":l({success:!1,error:new Error("Attachment was deleted")});break}})}catch(f){l({success:!1,error:f instanceof Error?f:new Error("Failed to fetch attachment")})}},async updateMessageReactions(i,u,l){if(!r||!n)return;const f=u._id,d=t().messagesByRoom[f]||[],g=d.findIndex(_=>_.id===i._id);if(g===-1)throw new Error("Message not found");const b=d[g].message,h=b.reactions||[];e(_=>N(_,w=>{w.messagesByRoom[f][g].message.reactions=l})),setTimeout(async()=>{try{await r.store.execute(`UPDATE ${u.messagesId} SET reactions = :reactions WHERE _id = :id`,{id:b._id,reactions:l})}catch(_){console.error("Error updating reactions, rolling back:",_),e(w=>N(w,I=>{I.messagesByRoom[f][g].message.reactions=h}))}},0)},async addReactionToMessage(i,u,l){if(!r||!n)return;if(!t().canPerformAction("canAddReaction")){console.warn("Permission denied: canAddReaction is false");return}const f=t().messagesByRoom[u._id],d=f.findIndex(h=>h.id===i._id);if(d===-1)throw new Error("Message not found");const g=f[d].message,b=[...g.reactions||[],l];await t().updateMessageReactions(g,u,b)},async removeReactionFromMessage(i,u,l){if(!r||!n)return;if(!t().canPerformAction("canRemoveOwnReaction")){console.warn("Permission denied: canRemoveOwnReaction is false");return}const f=t().messagesByRoom[u._id],d=f.findIndex(h=>h.id===i._id);if(d===-1)throw new Error("Message not found");const g=f[d].message,b=(g.reactions||[]).filter(h=>!(h.emoji===l.emoji&&h.userId===l.userId));await t().updateMessageReactions(g,u,b)}}};async function ft(e){const t=await Fe(e),r=document.createElement("canvas"),n=r.getContext("2d");if(!n)throw new Error("Failed to get 2D context");let{width:s,height:o}=t;return s>o?(o=V/s*o,s=V):(s=V/o*s,o=V),r.width=s,r.height=o,n.drawImage(t,0,0,s,o),r}function Fe(e){return new Promise((t,r)=>{const n=new FileReader;n.onload=s=>{const o=new Image;o.onload=()=>t(o),o.onerror=r,o.src=String(s.target?.result)},n.onerror=r,n.readAsDataURL(e)})}async function ze(e){return new Promise((t,r)=>{const n=s=>{s.toBlob(o=>o?t(o):r(new Error("Failed to convert to blob")),"image/jpeg",1)};if(e instanceof HTMLCanvasElement)n(e);else{const s=document.createElement("canvas");s.width=e.width,s.height=e.height;const o=s.getContext("2d");if(!o){r(new Error("Failed to get 2D context"));return}o.drawImage(e,0,0),n(s)}})}function fe(e,t,r,n){const s=new Date().toISOString(),o=t.replace(/\s/g,"-"),c=s.replace(/:/g,"-"),a=r==="file"?n.name.split(".").pop()||"bin":"jpg";return{filename:`${o}_${r}_${c}.${a}`,userId:e,username:t,fileformat:`.${a}`,filesize:n.size.toString(),timestamp:s,originalName:n.name}}function D(e){e&&!e.isCancelled&&e.cancel()}function dt(e){const t=A.useMemo(()=>(globalThis.__DITTO_CHAT_STORE__?console.log("Reusing EXISTING global chatStore instance"):(console.log("Creating NEW global chatStore instance"),globalThis.__DITTO_CHAT_STORE__=nt()((r,n)=>({...Ce(r,n,e),...at(r,n,e),...lt(r,n,e),...ge(r,n,e),activeRoomId:null,setActiveRoomId:s=>r({activeRoomId:s}),chatLogout:()=>{const s=n();D(s.roomsSubscription),D(s.roomsObserver),D(s.dmRoomsSubscription),D(s.dmRoomsObserver),Object.values(s.messageSubscriptionsByRoom||{}).map(o=>D(o)),Object.values(s.messageObserversByRoom||{}).map(o=>D(o)),D(s.userObserver),D(s.userSubscription),D(s.allUsersObserver),D(s.allUsersSubscription)}}))),globalThis.__DITTO_CHAT_STORE__),[e]);return Pe(t)}function mt(e){if(!globalThis.__DITTO_CHAT_STORE__)throw new Error("chatStore must be initialized before useDittoChatStore. use useDittoChat for initialization");const t=ct(e||(n=>n));return Pe(globalThis.__DITTO_CHAT_STORE__,t)}function yt(){return globalThis.__DITTO_CHAT_STORE__}function gt(){globalThis.__DITTO_CHAT_STORE__=void 0}E.DEFAULT_PERMISSIONS=ye,E.cancelSubscriptionOrObserver=D,E.createRBACSlice=ge,E.createRoomSlice=Ce,E.getChatStore=yt,E.resetChatStore=gt,E.useDittoChat=dt,E.useDittoChatStore=mt,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './slices/useRBAC';
|
|
2
|
+
export * from './slices/useRooms';
|
|
3
|
+
export * from './types/ChatUser';
|
|
4
|
+
export * from './types/Message';
|
|
5
|
+
export * from './types/MessageWithUser';
|
|
6
|
+
export * from './types/RBAC';
|
|
7
|
+
export * from './types/Room';
|
|
8
|
+
export * from './useChat';
|
|
9
|
+
export { getChatStore, resetChatStore } from './useChat';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StoreObserver, SyncSubscription } from '@dittolive/ditto';
|
|
2
|
+
import ChatUser from '../types/ChatUser';
|
|
3
|
+
import { CreateSlice } from '../useChat';
|
|
4
|
+
export interface ChatUserSlice {
|
|
5
|
+
usersLoading: boolean;
|
|
6
|
+
currentUser: ChatUser | null;
|
|
7
|
+
userObserver: StoreObserver | null;
|
|
8
|
+
userSubscription: SyncSubscription | null;
|
|
9
|
+
allUsers: ChatUser[];
|
|
10
|
+
allUsersObserver: StoreObserver | null;
|
|
11
|
+
allUsersSubscription: SyncSubscription | null;
|
|
12
|
+
addUser: (user: Omit<ChatUser, '_id'> & {
|
|
13
|
+
_id?: string;
|
|
14
|
+
}) => Promise<void>;
|
|
15
|
+
updateUser: (user: Partial<ChatUser> & {
|
|
16
|
+
_id: string;
|
|
17
|
+
}) => Promise<void>;
|
|
18
|
+
findUserById: (userId: string) => Promise<ChatUser | null>;
|
|
19
|
+
markRoomAsRead: (roomId: string) => Promise<void>;
|
|
20
|
+
toggleRoomSubscription: (roomId: string) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare const createChatUserSlice: CreateSlice<ChatUserSlice>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AttachmentToken, StoreObserver, SyncSubscription } from '@dittolive/ditto';
|
|
2
|
+
import Message, { Mention, Reaction } from '../types/Message';
|
|
3
|
+
import MessageWithUser from '../types/MessageWithUser';
|
|
4
|
+
import Room from '../types/Room';
|
|
5
|
+
import { CreateSlice } from '../useChat';
|
|
6
|
+
export interface MessageSlice {
|
|
7
|
+
messagesByRoom: Record<string, MessageWithUser[]>;
|
|
8
|
+
messageObserversByRoom: Record<string, StoreObserver | null>;
|
|
9
|
+
messageSubscriptionsByRoom: Record<string, SyncSubscription | null>;
|
|
10
|
+
messagesLoading: boolean;
|
|
11
|
+
messagesPublisher: (room: Room, retentionDays?: number) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Subscribe to messages for a specific room on-demand.
|
|
14
|
+
* Used for generated rooms (comment rooms) that need dynamic subscriptions.
|
|
15
|
+
*/
|
|
16
|
+
subscribeToRoomMessages: (roomId: string, messagesId: string, retentionDays?: number) => Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Unsubscribe from messages for a specific room.
|
|
19
|
+
* Cleans up subscription, observer, and messages from state.
|
|
20
|
+
*/
|
|
21
|
+
unsubscribeFromRoomMessages: (roomId: string) => void;
|
|
22
|
+
createMessage: (room: Room, text: string, mentions?: Mention[]) => Promise<void>;
|
|
23
|
+
saveEditedTextMessage: (message: Message, room: Room) => Promise<void>;
|
|
24
|
+
saveDeletedMessage: (message: Message, room: Room, type?: 'text' | 'image' | 'file') => Promise<void>;
|
|
25
|
+
createImageMessage: (room: Room, imageFile: File, text?: string) => Promise<void>;
|
|
26
|
+
createFileMessage: (room: Room, file: File, text?: string) => Promise<void>;
|
|
27
|
+
fetchAttachment: (token: AttachmentToken, onProgress: (progress: number) => void, onComplete: (result: AttachmentResult) => void) => void;
|
|
28
|
+
notificationHandler: ((message: MessageWithUser, room: Room) => void) | null;
|
|
29
|
+
registerNotificationHandler: (handler: (message: MessageWithUser, room: Room) => void) => void;
|
|
30
|
+
updateMessageReactions: (message: Message, room: Room, reactions: Reaction[]) => Promise<void>;
|
|
31
|
+
addReactionToMessage: (message: Message, room: Room, reaction: Reaction) => Promise<void>;
|
|
32
|
+
removeReactionFromMessage: (message: Message, room: Room, reaction: Reaction) => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
interface AttachmentResult {
|
|
35
|
+
success: boolean;
|
|
36
|
+
data?: Uint8Array;
|
|
37
|
+
metadata?: Record<string, string>;
|
|
38
|
+
error?: Error;
|
|
39
|
+
}
|
|
40
|
+
export declare const createMessageSlice: CreateSlice<MessageSlice>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PermissionKey, RBACConfig } from '../types/RBAC';
|
|
2
|
+
import { CreateSlice } from '../useChat';
|
|
3
|
+
export interface RBACSlice {
|
|
4
|
+
rbacConfig: RBACConfig;
|
|
5
|
+
canPerformAction: (action: PermissionKey) => boolean;
|
|
6
|
+
updateRBACConfig: (config: RBACConfig) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const createRBACSlice: CreateSlice<RBACSlice>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { StoreObserver, SyncSubscription } from '@dittolive/ditto';
|
|
2
|
+
import ChatUser from '../types/ChatUser';
|
|
3
|
+
import Room from '../types/Room';
|
|
4
|
+
import { CreateSlice } from '../useChat';
|
|
5
|
+
export interface CreateRoomOptions {
|
|
6
|
+
retentionDays?: number;
|
|
7
|
+
isGenerated?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RoomSlice {
|
|
10
|
+
rooms: Room[];
|
|
11
|
+
dmRooms: Room[];
|
|
12
|
+
generatedRooms: Room[];
|
|
13
|
+
roomsLoading: boolean;
|
|
14
|
+
roomsObserver: StoreObserver | null;
|
|
15
|
+
roomsSubscription: SyncSubscription | null;
|
|
16
|
+
dmRoomsObserver: StoreObserver | null;
|
|
17
|
+
dmRoomsSubscription: SyncSubscription | null;
|
|
18
|
+
createRoom: (name: string, options?: CreateRoomOptions) => Promise<void | Room>;
|
|
19
|
+
createDMRoom: (user: ChatUser) => Promise<void | Room>;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a generated room (comment room) with a specific ID.
|
|
22
|
+
* Generated rooms are hidden from the main chat list and used
|
|
23
|
+
* for programmatic use cases like comment threads.
|
|
24
|
+
*/
|
|
25
|
+
createGeneratedRoom: (id: string, name: string) => Promise<void | Room>;
|
|
26
|
+
/**
|
|
27
|
+
* Returns all public rooms, filtering out generated rooms (isGenerated === true).
|
|
28
|
+
*/
|
|
29
|
+
getPublicRooms: () => Room[];
|
|
30
|
+
}
|
|
31
|
+
export declare const createRoomSlice: CreateSlice<RoomSlice>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Attachment } from '@dittolive/ditto';
|
|
2
|
+
interface ChatUser {
|
|
3
|
+
_id?: string;
|
|
4
|
+
name: string;
|
|
5
|
+
subscriptions: Record<string, string | null>;
|
|
6
|
+
mentions: Record<string, string[]>;
|
|
7
|
+
profilePicture?: Attachment | null;
|
|
8
|
+
profilePictureThumbnail?: Attachment | null;
|
|
9
|
+
}
|
|
10
|
+
export default ChatUser;
|
|
11
|
+
export type { ChatUser };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Attachment } from '@dittolive/ditto';
|
|
2
|
+
export type Reaction = {
|
|
3
|
+
userId: string;
|
|
4
|
+
emoji: string;
|
|
5
|
+
unified?: string;
|
|
6
|
+
unifiedWithoutSkinTone?: string;
|
|
7
|
+
};
|
|
8
|
+
export interface Mention {
|
|
9
|
+
userId: string;
|
|
10
|
+
startIndex: number;
|
|
11
|
+
endIndex: number;
|
|
12
|
+
}
|
|
13
|
+
interface Message {
|
|
14
|
+
_id: string;
|
|
15
|
+
createdOn: string;
|
|
16
|
+
roomId: string;
|
|
17
|
+
text: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
largeImageToken?: Attachment | null;
|
|
20
|
+
thumbnailImageToken?: Attachment | null;
|
|
21
|
+
fileAttachmentToken?: Attachment | null;
|
|
22
|
+
archivedMessage?: string;
|
|
23
|
+
isArchived: boolean;
|
|
24
|
+
isEdited?: boolean;
|
|
25
|
+
isDeleted?: boolean;
|
|
26
|
+
reactions?: Reaction[];
|
|
27
|
+
mentions?: Mention[];
|
|
28
|
+
}
|
|
29
|
+
export default Message;
|
|
30
|
+
export type { Message };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PermissionKey = 'canCreateRoom' | 'canEditOwnMessage' | 'canDeleteOwnMessage' | 'canAddReaction' | 'canRemoveOwnReaction' | 'canMentionUsers' | 'canSubscribeToRoom';
|
|
2
|
+
export interface RBACConfig {
|
|
3
|
+
canCreateRoom?: boolean;
|
|
4
|
+
canEditOwnMessage?: boolean;
|
|
5
|
+
canDeleteOwnMessage?: boolean;
|
|
6
|
+
canAddReaction?: boolean;
|
|
7
|
+
canRemoveOwnReaction?: boolean;
|
|
8
|
+
canMentionUsers?: boolean;
|
|
9
|
+
canSubscribeToRoom?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const DEFAULT_PERMISSIONS: Required<RBACConfig>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Room {
|
|
2
|
+
_id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
messagesId: string;
|
|
5
|
+
collectionId?: string;
|
|
6
|
+
createdBy: string;
|
|
7
|
+
createdOn: string;
|
|
8
|
+
isGenerated: boolean;
|
|
9
|
+
participants?: string[];
|
|
10
|
+
retentionDays?: number;
|
|
11
|
+
}
|
|
12
|
+
export default Room;
|
|
13
|
+
export type { Room };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Ditto, StoreObserver, SyncSubscription } from '@dittolive/ditto';
|
|
2
|
+
import { StoreApi } from 'zustand/vanilla';
|
|
3
|
+
import { ChatUserSlice } from './slices/useChatUser';
|
|
4
|
+
import { MessageSlice } from './slices/useMessages';
|
|
5
|
+
import { RBACSlice } from './slices/useRBAC';
|
|
6
|
+
import { RoomSlice } from './slices/useRooms';
|
|
7
|
+
import { RBACConfig } from './types/RBAC';
|
|
8
|
+
export type DittoConfParams = {
|
|
9
|
+
ditto: Ditto | null;
|
|
10
|
+
userId: string;
|
|
11
|
+
userCollectionKey: string;
|
|
12
|
+
retentionDays?: number;
|
|
13
|
+
rbacConfig?: RBACConfig;
|
|
14
|
+
notificationHandler?: (title: string, description: string) => void;
|
|
15
|
+
};
|
|
16
|
+
export type CreateSlice<T> = (set: StoreApi<ChatStore>['setState'], get: StoreApi<ChatStore>['getState'], params: DittoConfParams) => T;
|
|
17
|
+
export type ChatStore = RoomSlice & ChatUserSlice & MessageSlice & RBACSlice & {
|
|
18
|
+
activeRoomId: string | number | null;
|
|
19
|
+
setActiveRoomId: (roomId: string | number | null) => void;
|
|
20
|
+
chatLogout: () => void;
|
|
21
|
+
};
|
|
22
|
+
declare global {
|
|
23
|
+
var __DITTO_CHAT_STORE__: StoreApi<ChatStore> | undefined;
|
|
24
|
+
}
|
|
25
|
+
export declare function cancelSubscriptionOrObserver(subscription: SyncSubscription | StoreObserver | null): void;
|
|
26
|
+
export declare function useDittoChat(params: DittoConfParams): ChatStore;
|
|
27
|
+
export declare function useDittoChatStore<T = Partial<ChatStore>>(selector?: (state: ChatStore) => T): T;
|
|
28
|
+
/**
|
|
29
|
+
* Get the global chat store instance
|
|
30
|
+
* Useful for debugging or accessing the store outside of React components
|
|
31
|
+
*/
|
|
32
|
+
export declare function getChatStore(): StoreApi<ChatStore> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Reset the global chat store
|
|
35
|
+
* Useful for testing or when you need to completely reinitialize
|
|
36
|
+
*/
|
|
37
|
+
export declare function resetChatStore(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ChatStore } from '../src/useChat';
|
|
2
|
+
export type MockDitto = ReturnType<typeof createMockDitto>;
|
|
3
|
+
export declare const createMockDitto: () => {
|
|
4
|
+
store: {
|
|
5
|
+
execute: import("vitest").Mock<(...args: any[]) => any>;
|
|
6
|
+
registerObserver: import("vitest").Mock<(...args: any[]) => any>;
|
|
7
|
+
newAttachment: import("vitest").Mock<(...args: any[]) => any>;
|
|
8
|
+
fetchAttachment: import("vitest").Mock<(...args: any[]) => any>;
|
|
9
|
+
};
|
|
10
|
+
sync: {
|
|
11
|
+
registerSubscription: import("vitest").Mock<(...args: any[]) => any>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export declare const createTestStore: (mockDitto: MockDitto | null) => import("zustand").StoreApi<ChatStore>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,30 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dittolive/ditto-chat-core",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"types": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "src/index.ts",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"development": "./src/index.ts",
|
|
11
|
+
"types": "./dist/src/index.d.ts",
|
|
12
|
+
"default": "./dist/index.es.js"
|
|
13
|
+
},
|
|
14
|
+
"./dist/types/*": {
|
|
15
|
+
"development": "./src/types/*.ts",
|
|
16
|
+
"types": "./dist/src/types/*.d.ts",
|
|
17
|
+
"default": "./dist/types/*.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
7
20
|
"files": [
|
|
8
21
|
"dist"
|
|
9
22
|
],
|
|
10
|
-
"
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"prepublishOnly": "npm run build"
|
|
13
|
-
},
|
|
14
|
-
"publishConfig": {
|
|
15
|
-
"access": "public"
|
|
16
|
-
},
|
|
23
|
+
"author": "Ditto",
|
|
17
24
|
"license": "MIT",
|
|
18
25
|
"repository": {
|
|
19
26
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/
|
|
27
|
+
"url": "https://github.com/getditto/DittoChat.git"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
31
|
+
"clean": "rm -rf dist",
|
|
32
|
+
"lint": "eslint .",
|
|
33
|
+
"lint:fix": "eslint . --fix",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest watch",
|
|
36
|
+
"test:coverage": "vitest run --coverage",
|
|
37
|
+
"typecheck": "tsc --noEmit --skipLibCheck"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@dittolive/ditto": "^4.12.3",
|
|
41
|
+
"react": "^18 || ^19",
|
|
42
|
+
"zustand": "^5.0.8"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.39.1",
|
|
46
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
47
|
+
"@testing-library/react": "^16.3.0",
|
|
48
|
+
"@types/react": "^19.2.2",
|
|
49
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
50
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
51
|
+
"@vitest/ui": "^3.2.4",
|
|
52
|
+
"eslint": "^9.39.1",
|
|
53
|
+
"eslint-plugin-react": "^7.37.5",
|
|
54
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
55
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
56
|
+
"eslint-plugin-sort-keys-fix": "^1.1.2",
|
|
57
|
+
"happy-dom": "^20.0.10",
|
|
58
|
+
"jsdom": "^24.1.3",
|
|
59
|
+
"typescript": "^5.6.3",
|
|
60
|
+
"typescript-eslint": "^8.18.2",
|
|
61
|
+
"vite": "^7.1.7",
|
|
62
|
+
"vitest": "^3.2.4",
|
|
63
|
+
"zustand": "^5.0.8"
|
|
21
64
|
},
|
|
22
65
|
"keywords": [
|
|
66
|
+
"react",
|
|
67
|
+
"typescript",
|
|
23
68
|
"ditto",
|
|
24
|
-
"chat",
|
|
25
|
-
"
|
|
69
|
+
"ditto-chat-core",
|
|
70
|
+
"ditto-chat"
|
|
26
71
|
],
|
|
27
|
-
"
|
|
28
|
-
"
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@dittolive/ditto": "^4.12.3",
|
|
74
|
+
"immer": "^10.2.0",
|
|
75
|
+
"uuid": "^13.0.0"
|
|
76
|
+
},
|
|
77
|
+
"overrides": {
|
|
78
|
+
"glob": "^10.5.0"
|
|
29
79
|
}
|
|
30
80
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './slices/useRBAC'
|
|
2
|
+
export * from './slices/useRooms'
|
|
3
|
+
export * from './types/ChatUser'
|
|
4
|
+
export * from './types/Message'
|
|
5
|
+
export * from './types/MessageWithUser'
|
|
6
|
+
export * from './types/RBAC'
|
|
7
|
+
export * from './types/Room'
|
|
8
|
+
export * from './useChat'
|
|
9
|
+
export { getChatStore, resetChatStore } from './useChat'
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @dittolive/ditto-chat-core
|
|
4
|
-
*
|
|
5
|
-
* Placeholder initial release to claim npm namespace.
|
|
6
|
-
* Real implementation coming soon.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.VERSION = void 0;
|
|
10
|
-
exports.placeholder = placeholder;
|
|
11
|
-
exports.VERSION = "0.0.1";
|
|
12
|
-
function placeholder() {
|
|
13
|
-
return "This is a placeholder package for @dittolive/ditto-chat-core";
|
|
14
|
-
}
|