Skip to content

Commit

Permalink
feat: Add utils for public link shares
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Jun 19, 2024
1 parent dedb0f8 commit 7cb6dd8
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 4 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# @nextcloud/sharing
# `@nextcloud/sharing`

[![npm](https://img.shields.io/npm/v/@nextcloud/sharing.svg)](https://www.npmjs.com/package/@nextcloud/sharing)

Common sharing code for the front-end
Common front-end utils for files sharing on Nextcloud and Nextcloud apps.

## Installation

```
npm i -S @nextcloud/sharing
```

## Usage

There are two entry points provided:

- The main entry point `@nextcloud/sharing` provides general utils for file sharing
- The _public_ entry point `@nextcloud/sharing/public` provides utils for handling public file shares
122 changes: 122 additions & 0 deletions lib/publicShare.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
* @vitest-environment happy-dom

Check warning on line 4 in lib/publicShare.spec.ts

View workflow job for this annotation

GitHub Actions / NPM lint

Invalid JSDoc tag name "vitest-environment"
*/

import { beforeEach, describe, expect, test, vi } from 'vitest'

const initialState = vi.hoisted(() => ({ loadState: vi.fn() }))
vi.mock('@nextcloud/initial-state', () => initialState)

const mockPublicShare = () => {
initialState.loadState.mockImplementation((app, key) => {
if (key === 'isPublic') {
return true
} else if (key === 'sharingToken') {
return 'modern-token'
}
throw new Error('Unexpected loadState')
})
}

const mockLegacyPublicShare = () => {
initialState.loadState.mockImplementationOnce(() => null)

const input = document.createElement('input')
input.id = 'isPublic'
input.name = 'isPublic'
input.type = 'hidden'
input.value = '1'
document.body.appendChild(input)

const token = document.createElement('input')
token.id = 'sharingToken'
token.type = 'hidden'
token.value = 'legacy-token'
document.body.appendChild(token)
}

describe('isPublicShare', () => {
beforeEach(() => {
vi.resetModules()
vi.resetAllMocks()
// reset JSDom
document.body.innerHTML = ''
})

const isPublicShare = async () => {
const { isPublicShare: publicShare } = await import('./publicShare')
return publicShare()
}

test('no public share', async () => {
initialState.loadState.mockImplementation(() => null)

expect(await isPublicShare()).toBe(false)
expect(initialState.loadState).toBeCalledWith(
'files_sharing',
'isPublic',
null,
)
})

test('public share', async () => {
mockPublicShare()

expect(await isPublicShare()).toBe(true)
expect(initialState.loadState).toBeCalledWith(
'files_sharing',
'isPublic',
null,
)
})

test('legacy public share', async () => {
mockLegacyPublicShare()

expect(await isPublicShare()).toBe(true)
})
})

describe('getSharingToken', () => {
beforeEach(() => {
vi.resetModules()
vi.resetAllMocks()
// reset happy-dom
document.body.innerHTML = ''
})

const getSharingToken = async () => {
const { getSharingToken: sharingToken } = await import('./publicShare')
return sharingToken()
}

test('no public share', async () => {
initialState.loadState.mockImplementation(() => null)

expect(await getSharingToken()).toBe(null)
expect(initialState.loadState).toBeCalledWith(
'files_sharing',
'sharingToken',
null,
)
})

test('public share', async () => {
mockPublicShare()

expect(await getSharingToken()).toBe('modern-token')
expect(initialState.loadState).toBeCalledWith(
'files_sharing',
'sharingToken',
null,
)
})

test('legacy public share', async () => {
mockLegacyPublicShare()

expect(await getSharingToken()).toBe('legacy-token')
})
})
30 changes: 30 additions & 0 deletions lib/publicShare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { loadState } from '@nextcloud/initial-state'

/**
* Check if the current page is on a public share
*/
export function isPublicShare(): boolean {
// check both the new initial state version and fallback to legacy input
return (
loadState<boolean | null>('files_sharing', 'isPublic', null) ??
document.querySelector(
'input#isPublic[type="hidden"][name="isPublic"][value="1"]',
) !== null
)
}

/**
* Get the sharing token for the current public share
*/
export function getSharingToken(): string | null {
return (
loadState<string | null>('files_sharing', 'sharingToken', null) ??
document.querySelector<HTMLInputElement>('input#sharingToken[type="hidden"]')
?.value ??
null
)
}
45 changes: 45 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
],
"scripts": {
"build": "vite --mode production build",
"build:doc": "typedoc --out dist/doc lib && touch dist/doc/.nojekyll",
"build:doc": "typedoc --out dist/doc lib/index.ts lib/publicShare.ts && touch dist/doc/.nojekyll",
"dev": "vite --mode development build",
"dev:watch": "vite --mode development build --watch",
"format": "prettier --check .",
Expand All @@ -47,6 +47,9 @@
"prettier"
]
},
"dependencies": {
"@nextcloud/initial-state": "^2.2.0"
},
"devDependencies": {
"@nextcloud/browserslist-config": "^3.0.1",
"@nextcloud/eslint-config": "^8.4.1",
Expand All @@ -56,6 +59,7 @@
"@vitest/coverage-v8": "^1.6.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"happy-dom": "^14.12.0",
"prettier": "^3.3.2",
"typedoc": "^0.25.13",
"typescript": "^5.4.5",
Expand Down
4 changes: 3 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { join } from 'node:path'
export default createLibConfig(
{
index: join(__dirname, 'lib', 'index.ts'),
}, {
public: join(__dirname, 'lib', 'publicShare.ts'),
},
{
config: {
test: {
coverage: {
Expand Down

0 comments on commit 7cb6dd8

Please sign in to comment.