cozy-sharing 37.2.9 → 37.2.11

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/AGENTS.md ADDED
@@ -0,0 +1,21 @@
1
+ # cozy-sharing — agent rules
2
+
3
+ ## Keep README.md in sync with the code
4
+
5
+ The implementation is the behavioral source of truth. `README.md` documents the implemented behavior and usage, and MUST be updated in the same commit/PR whenever the behavior or usage changes.
6
+
7
+ ### When to update the README
8
+
9
+ - **Feature flags** — a flag is added, removed, renamed, or its default changes → update the "Feature flags" table.
10
+ - **Feature behavior or conditions** — a permission rule, a restriction (e.g. `hasSharedParent` / `hasSharedChild`), a link lifecycle step, or a gating condition changes → update the matching "Feature conditions" table.
11
+ - **Architecture** — the `ShareModal` routing, a new modal/dialog, or the `SharingProvider` context shape changes → update "Architecture".
12
+
13
+ ### When NOT to update
14
+
15
+ - Pure refactors with no behavior change.
16
+ - Bug fixes that don't change documented behavior.
17
+ - Test-only or style-only changes.
18
+
19
+ ### Rule of thumb
20
+
21
+ If the code and the README disagree after your change, the change is incomplete — fix the README before committing.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [37.2.11](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.10...cozy-sharing@37.2.11) (2026-07-30)
7
+
8
+ **Note:** Version bump only for package cozy-sharing
9
+
10
+ ## [37.2.10](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.9...cozy-sharing@37.2.10) (2026-07-30)
11
+
12
+ **Note:** Version bump only for package cozy-sharing
13
+
6
14
  ## [37.2.9](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.8...cozy-sharing@37.2.9) (2026-07-29)
7
15
 
8
16
  **Note:** Version bump only for package cozy-sharing
package/README.md CHANGED
@@ -1,83 +1,230 @@
1
- # Test 'n play
1
+ # cozy-sharing
2
2
 
3
- Open the playgrounds in cozy-libs and run `yarn start`
3
+ React library that provides sharing UI and logic for Cozy / Twake Drive applications.
4
4
 
5
- # How to use the lib
5
+ It wraps the Cozy Stack sharing API and exposes ready-to-use components (modal, button, banners), hooks, and context for building sharing experiences.
6
6
 
7
- In your app, you have to :
7
+ ***
8
8
 
9
- - import the Provider: `import SharingProvider, { ShareButton, ShareModal } from 'cozy-sharing'`
10
- - import the stylesheet.css: `import 'cozy-sharing/dist/stylesheet.css'`
9
+ ## Installation
11
10
 
12
- ## Using the built-in components
11
+ ```bash
12
+ yarn add cozy-sharing
13
+ ```
14
+
15
+ In your app, you must:
16
+
17
+ - wrap the component tree with `SharingProvider`
18
+ - import the stylesheet: `import 'cozy-sharing/dist/stylesheet.css'`
19
+
20
+ ***
13
21
 
14
- Some of the exposed components are fully featured components, ready to render. They need a `SharingProvider` above them in the render tree and the imported stylesheet for their styles.
22
+ ## Usage
15
23
 
24
+ ### Provider
25
+
26
+ ```jsx
27
+ import SharingProvider from 'cozy-sharing'
28
+
29
+ <SharingProvider doctype="io.cozy.files" documentType="Files">
30
+ <App />
31
+ </SharingProvider>
16
32
  ```
33
+
34
+ **Props:**
35
+
36
+ | Prop | Required | Description |
37
+ |------|----------|-------------|
38
+ | `doctype` | yes | The cozy doctype (e.g. `io.cozy.files`) |
39
+ | `documentType` | no | Human-readable type for i18n keys (default `'Document'`) |
40
+ | `onShared` | no | Callback fired after a share is created |
41
+ | `isPublic` | no | Set to `true` when rendering a public page (disables fetching) |
42
+ | `previewPath` | no | Custom preview path sent to the share creation |
43
+
44
+ ### Built-in components
45
+
46
+ Surrounding application code (imports, state, props) is omitted for brevity.
47
+
48
+ ```jsx
17
49
  import { ShareModal } from 'cozy-sharing'
18
50
 
19
51
  const ToggleModal = () => {
20
- const [isModalDisplayed, setIsModalDisplayed] = useState(false)
21
-
52
+ const [isOpen, setIsOpen] = useState(false)
22
53
  return (
23
54
  <div>
24
- <Button onClick={() => setIsModalDisplayed(true)}>Open modal</Button>
25
- {isModalDisplayed && <ShareModal document={doc} />}
55
+ <Button onClick={() => setIsOpen(true)}>Share</Button>
56
+ {isOpen && <ShareModal document={doc} onClose={() => setIsOpen(false)} />}
26
57
  </div>
27
58
  )
28
59
  }
29
60
  ```
30
61
 
31
- Other components accept a render prop as children that receive some information from the sharing context.
62
+ ### Render-prop components
32
63
 
33
- ```
64
+ ```jsx
34
65
  import { SharedDocument } from 'cozy-sharing'
35
66
 
67
+ const MyComp = () => (
68
+ <SharedDocument docId="123">
69
+ {({ isShared, link }) => (isShared ? link : 'Not shared yet')}
70
+ </SharedDocument>
71
+ )
72
+ ```
73
+
74
+ ### Hooks
75
+
76
+ ```jsx
77
+ import { useSharingContext } from 'cozy-sharing'
78
+
36
79
  const MyComp = () => {
37
- return (
38
- <SharedDocument docId='123'>
39
- {({ isShared, link }) => (
40
- {isShared ? link : 'Not shared yet'}
41
- )}
42
- </SharedDocument>
43
- )
80
+ const { share } = useSharingContext()
81
+ return <Button onClick={() => share({ document, recipients, ... })}>Share</Button>
44
82
  }
45
83
  ```
46
84
 
47
- ## Usage with hooks
85
+ ***
48
86
 
49
- `cozy-sharing` can now be used with hooks as well:
87
+ ## How it works
50
88
 
51
- ```
52
- import { SharingContext } from 'cozy-sharing'
89
+ ### Permission model
53
90
 
54
- const MyComp = () => {
55
- const { share } = useContext(SharingContext)
91
+ The UI exposes two roles, but the stack has more granular verb control:
56
92
 
57
- return <Button onClick={() => share(document, recipients, sharingType, description)}>Share</Button>
58
- }
59
- ```
93
+ | Role | Verbs | Description |
94
+ |------|-------|-------------|
95
+ | **Viewer** | `GET` | Read-only access |
96
+ | **Editor** | `GET, POST, PUT, PATCH` | Read + write + share management |
97
+
98
+ The `write` permission allows editing content, uploading files, deleting files (to the owner's trash), and managing share members (add/remove members, change permissions, view/manage link).
99
+
100
+ ### Link sharing (public URL)
101
+
102
+ Creates a public URL accessible without a Twake account. One link per resource at any time.
103
+
104
+ **Options (set in `ShareRestrictionModal` or `ShareLinkAccessModal`):**
105
+
106
+ | Option | Default | Details |
107
+ |--------|---------|---------|
108
+ | Permission | `readOnly` | Can be changed to `write` |
109
+ | Expiration date | Off | Toggle; defaults to 30 days ahead when enabled; expires at end of that day |
110
+ | Password | Off | Minimum 4 characters; owner communicates it out-of-band |
111
+
112
+ **Lifecycle:**
113
+
114
+ - Deactivating a link revokes the current permissions. Reactivating generates a **new URL** — the old one stays invalid.
115
+ - Deleting the root resource revokes all associated shares (link and email).
116
+ - Link sharing coexists with email sharing on the same resource.
60
117
 
61
- # Share and send mail in development
118
+ ### Email sharing (cozy-to-cozy)
119
+
120
+ Invites specific users or groups with individualized permissions. Two technical modes:
121
+
122
+ - **Cozy-to-cozy sharing** (classic) — data is **replicated** to each recipient's Cozy.
123
+ - **Shared drives** (shared folders, team drives) — recipients access files at the **owner's instance** (no replication).
124
+
125
+ In both cases, content appears in the recipient's **Sharing tab**.
126
+
127
+ **By recipient status:**
128
+
129
+ | Recipient status | Behavior |
130
+ |-----------------|----------|
131
+ | Trusted contact (same org or previously accepted) | Content appears directly in the Sharing tab; notification sent |
132
+ | Has Twake account, not in contacts | Invitation email → login → Sharing tab |
133
+ | **No Twake account** | ❌ Not supported today |
134
+ | **Different SSO** | ❌ Not supported today |
135
+ | Not trusted (guest in org) | ❌ Only if added manually; not automated today |
136
+
137
+ ### Cohabitation
138
+
139
+ Link and email sharing can coexist on the same resource. The recipient's permission is the most permissive when both apply.
140
+
141
+ ***
142
+
143
+ ## Feature conditions
144
+
145
+ ### Link sharing
146
+
147
+ | Condition | Rule | Source |
148
+ |-----------|------|--------|
149
+ | Who can create/manage | Owner or Editor (`canReshare` = owner, or `open_sharing && !read_only`) | `canReshare()` in `state.js` |
150
+ | Document type restriction | Hidden when `documentType === 'Organizations'` | `ShareModal.jsx:33` |
151
+ | Albums | Link-only, read-only permissions only | `ShareModal.jsx:30`, `link.js:3` |
152
+ | Editing rights box | Hidden when `drive.federated-shared-folder.enabled` flag is true | `BoxEditingRights.jsx:54` |
153
+ | Expiration date toggle default | Controlled by `sharing.date-toggle.enabled` flag (default: false) | `ShareRestrictionModal.jsx:62` |
154
+ | Link deactivation | Revokes the permission document; regenerates a new URL on reactivation | `SharingProvider.jsx:635` |
155
+ | Password | 4+ characters; no limit on attempts | `ShareLinkSettings.jsx:10` |
156
+ | Guest access | Via `/public` URL with `sharecode`; no auth required | `helpers.js:106` |
157
+
158
+ ### Email sharing
159
+
160
+ | Condition | Rule | Source |
161
+ |-----------|------|--------|
162
+ | Who can share | Owner or Editor (`canReshare`) | `SharingProvider.jsx` |
163
+ | **Core constraint** | Disabled if `hasSharedParent` OR `hasSharedChild` — a subfolder of an email-shared folder cannot be shared by email independently | `ShareModal.jsx:32`, `state.js:508-523` |
164
+ | Recipients limit | Default 100; overridable via `sharing.recipients-limit` flag | `helpers/recipients.js:33` |
165
+ | Recipient display mode | `sharing.show-recipient-groups` flag: `true` = groups as units, `false` = spread group members | `ShareByEmail.jsx:31` |
166
+ | Read-only sharing | If the existing sharing is read-only, only the `readOnly` option is offered | `ShareByEmail.jsx:46-57` |
167
+ | Contacts shown | Only contacts with a defined `email` or `cozy` URL | `helpers/recipients.js:72-73` |
168
+ | Group sharing | All members get the same permission; dynamic membership (add/remove) propagates | `state.js:414-451` |
169
+
170
+ ### Shared drives & federated folders
171
+
172
+ | Condition | Rule | Source |
173
+ |-----------|------|--------|
174
+ | Org shared drive | `isOrgSharedDrive` = `sharing.drive === true && sharing.org_drive === true` | `state.js:324` |
175
+ | `canLeave` | `false` for org shared drives | `state.js:335` |
176
+ | `canReshare` | `!org_drive && !read_only` for drives; `open_sharing && !read_only` for folders | `state.js:342` |
177
+ | Federated folder modal | Enabled by `drive.federated-shared-folder.enabled` flag | `ShareModal.jsx:39` |
178
+ | Email sharing in federated folder | Disabled for files inside a federated shared folder (has `driveId` but is not the root) | `FederatedFolderModal.jsx:122-126` |
179
+ | Link sharing in federated folder | Uses `getFederatedShareLink` which resolves the owner's instance URL | `FederatedFolderModal.jsx:108-110` |
180
+
181
+ ### Share management
182
+
183
+ | Condition | Rule | Source |
184
+ |-----------|------|--------|
185
+ | Editable modal | `isEditable = !byDocId[doc] \|\| isOwner(doc) \|\| canReshare(doc)` | `ShareModal.jsx:35-36` |
186
+ | Non-editable view | `SharingDetailsModal` — read-only view of members, with revoke-self | `ShareModal.jsx:53-62` |
187
+ | Updating member type | `updateSharingMemberType` calls `setReadOnly` / `setReadWrite` on the stack | `SharingProvider.jsx:361-420` |
188
+ | Revoke self | Available to all recipients | `SharingProvider.jsx:349-353` |
189
+ | Revoke group | Available to owner/editor; removes all group members at once | `SharingProvider.jsx:336-347` |
190
+
191
+ ### Native mobile sharing
192
+
193
+ | Condition | Rule | Source |
194
+ |-----------|------|--------|
195
+ | Availability | Only in flagship app (`isFlagshipApp()`) when `shareFiles` intent is available | `NativeFileSharingProvider.jsx:26-27` |
196
+ | Restriction | Files only (not directories) | `shareNative.js:42` |
197
+
198
+ ***
199
+
200
+ ## Feature flags
201
+
202
+ | Flag | Default | Effect |
203
+ |------|---------|--------|
204
+ | `cozy.hide-sharing-cozy-to-cozy` | `false` | When `true`, hides cozy-to-cozy sharing entirely; only link sharing is available |
205
+ | `drive.federated-shared-folder.enabled` | `false` | Enables federated folder modal (shared drives across instances) |
206
+ | `sharing.date-toggle.enabled` | `false` | Default state of the expiration date toggle when creating a new link |
207
+ | `sharing.show-recipient-groups` | `false` | When `true`, groups are shown as distinct recipients; when `false`, group members are spread |
208
+ | `sharing.recipients-limit` | `100` | Maximum number of recipients per document |
209
+ | `signup-url` / `signup.url` | `https://sign-up.twake.app` | URL used for the sharing banner "create account" call-to-action |
210
+
211
+ ## Development
212
+
213
+ ### Share and send mail in development
62
214
 
63
215
  Cozy apps let users [share documents from cozy to cozy](https://github.com/cozy/cozy-stack/blob/master/docs/sharing.md#cozy-to-cozy-sharing).
64
216
 
65
- Meet Alice and Bob.
66
- Alice wants to share a folder with Bob.
67
- Alice clicks on the share button and fills in the email input with Bob's email address.
68
- Bob receives an email with a *« Accept the sharing »* button.
69
- Bob clicks on that button and is redirected to Alice's cozy to enter his own cozy url to link both cozys.
70
- Bob sees Alice's shared folder in his own cozy.
217
+ Meet Alice and Bob. Alice wants to share a folder with Bob. Alice clicks on the share button and fills in the email input with Bob's email address. Bob receives an email with a *"Accept the sharing"* button. Bob clicks on that button and is redirected to Alice's cozy to enter his own cozy url to link both cozys. Bob sees Alice's shared folder in his own cozy.
71
218
 
72
- 🤔 But how could we do this scenario on development environment?
219
+ But how could we do this scenario on development environment?
73
220
 
74
- ## With the docker image
221
+ #### With the docker image
75
222
 
76
223
  If you develop with the [cozy-app-dev docker image](https://github.com/cozy/cozy-stack/blob/master/docs/client-app-dev.md#with-docker), [MailHog](https://github.com/mailhog/MailHog) is running inside it to catch emails.
77
224
 
78
225
  If cozy-stack has to send an email, MailHog catches it and exposes it on its web interface on <http://cozy.tools:8025/>.
79
226
 
80
- ## With the binary cozy-stack
227
+ #### With the binary cozy-stack
81
228
 
82
229
  If you develop with the [cozy-stack CLI](https://github.com/cozy/cozy-stack/blob/master/docs/cli/cozy-stack.md), you have to run [MailHog](https://github.com/mailhog/MailHog) on your computer and tell `cozy-stack serve` where to find the mail server with some [options](https://github.com/cozy/cozy-stack/blob/master/docs/cli/cozy-stack_serve.md#options):
83
230
 
@@ -89,6 +236,19 @@ If you develop with the [cozy-stack CLI](https://github.com/cozy/cozy-stack/blob
89
236
 
90
237
  Then simply run `mailhog` and open <http://cozy.tools:8025/>.
91
238
 
92
- ## Retrieve sent emails
239
+ #### Retrieve sent emails
93
240
 
94
241
  With MailHog, **every email** sent by cozy-stack is caught. That means the email address *does not have to be a real one*, ie. `bob@cozy`, `bob@cozy.tools` are perfectly fine. It *could be a real one*, but the email will not reach the real recipient's inbox, say `contact@cozycloud.cc`.
242
+
243
+ ***
244
+
245
+ ## Architecture
246
+
247
+ The share modal's component tree is documented in [`docs/share-modal-architecture.md`](docs/share-modal-architecture.md).
248
+
249
+ Key decisions:
250
+
251
+ - `ShareModal` checks `isEditable` → `EditableSharingModal` (editable) or `SharingDetailsModal` (read-only).
252
+ - `EditableSharingModal` renders `ShareModal` (the dumb component), which decides `ShareDialogCozyToCozy` vs `ShareDialogOnlyByLink` based on flags and context.
253
+ - `ShareDialogTwoStepsConfirmationContainer` wraps the cozy-to-cozy dialog when recipients need confirmation (untrusted contacts).
254
+ - Federated mode: when `drive.federated-shared-folder.enabled` is true, `FederatedFolderModal` replaces the standard `EditableSharingModal`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "37.2.9",
3
+ "version": "37.2.11",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -59,7 +59,7 @@
59
59
  "cozy-intent": "^2.31.1",
60
60
  "cozy-minilog": "^3.10.1",
61
61
  "cozy-ui": "^140.5.0",
62
- "cozy-ui-plus": "^12.3.3",
62
+ "cozy-ui-plus": "^12.3.5",
63
63
  "jest": "30.3.0",
64
64
  "jest-environment-jsdom": "30.3.0",
65
65
  "react": "16.12.0",
@@ -85,5 +85,5 @@
85
85
  "sideEffects": [
86
86
  "*.css"
87
87
  ],
88
- "gitHead": "f173ec5ce78ad2bf5f9abd174f8cef980a8ebfc7"
88
+ "gitHead": "163abe4aea6ccd37e5b6a256f30b081be81c0661"
89
89
  }