wix-photo-sharing-client
v1.0.312
Published
Photo Sharing API JS Client
Downloads
40
Maintainers
Keywords
Readme
About
Client library for Photo Sharing API based on fetch
.
Usage
import {PhotoSharingClient, PhotoSharingClientOptions} from '@wix/photo-sharing-client';
const fetch = window.fetch //in web or node-fetch in nodejs
const clientOptions: PhotoSharingClientOptions = PhotoSharingClientOptionsBuilder
.newBuilder({ fetch, authorization: signedInstance })
.crossOrigin() // for making requests from site domain use sameOrigin()
.sameOrigin(baseUrl) // baseUrl is optional and defaults to ''
.build();
const photoSharingClient = PhotoSharingClient.create(clientOptions);
// Interacting with server
try {
const appSettings = await photoSharingClient.getAppSettings();
} catch (e) {
console.log('Error with code: ', e.errorCode);
}
Validation errors
Server validates requests. When invalid request is made 400 HTTP error is handled by the library:
it('invalid request', async () => {
try {
await photoSharingClient.viewVisualMedia([{
itemId: '3a992b33-97b7-413b-be11-bdbd65197fb9',
incrementViewCount: true
}])
} catch (e) {
expect(e.errorCode).to.be.equal('InvalidRequest');
expect(e.message).to.contain('is invalid');
}
});
e.message
holds explanation of the error that was sent by server.
Enums
For Enum fields you can import Enum constants or use strings that match constant name.
Constants:
import {wix} from '@wix/photo-sharing-client';
layoutSettings: {
layout: wix.photosharing.api.v1.settings.Layout.MASONRY,
textAlignment: wix.photosharing.api.v1.settings.TextAlignment.LEFT
}
Strings:
import {wix} from '@wix/photo-sharing-client';
layoutSettings: {
layout: 'MASONRY'
}
Longs
Big integers are represented as https://github.com/dcodeIO/long.js longs. Example is visual media size in bytes.
Root Album Id
Root album is a special album. It is not physicaly stored anywhere. Root albums's parent is root album itself.
Each instance of photo sharing can have different root album id. Root album id is provided in getAppSettings
response.
Default Album Id
Default album is auto generated album present in all photo sharing instances.
You can set name, description and cover for it normally, use it to upload images, but you can't remove it.
Default album id is provided in getAppSettings
response.
App Settings
App settings entity contains all settings for site's or group's Photo Sharing TPA.
{
layout: {
layout: 'MASONRY',
galleryAlignment: 'LEFT',
photoSize: 10,
spacing: 64
},
display: {
enableComments: true,
enableTagging: true,
enableViewsCounter: true,
defaultSort: 'MOST_RECENT',
galleryTitle: null
},
upload: {
allowVideoUploads: true
},
design: {
galleryButtonCornerRadius: 0,
photoItemBorderWidth: 1,
photoItemCornerRadius: 0
},
membersAreaDesign: {
borderWidth: 42,
cornerRadius: 41
}
}
Updates existing or default app settings. Only keys present in json are updated (Object.keys
).
const settings = {
social: {
showViewsCounter: true,
}
};
const {settings} = await photoSharingClient.updateAppSettings(settings);
Get default or existing settings:
const {settings, rootFolderId, defaultAlbumId, supportedExtensions} = await photoSharingClient.getAppSettings();
Supported Extensions
photoShareClient.getAppSettings()
also returns supportedExtensions
that should be used to limit what user can upload. This list is also used in validation during upload. Example :
{
"supportedExtensions": [{
"category": "VIDEO|IMAGE",
"extensions": ["pdf", "xml"]
}]
}
Comments Component context
Comments library requires authorization token, which is provided when retrieving app settings
componentContext
from photoShareClient.getAppSettings().commentsContext
must be passed to comments module requests. Example :
{
componentContext: 'comments-context'
}
Photo Sharing item
Main domain entity is Photo Sharing Item.
Photo Sharing Entity
{
id: "uuid",
name: "string",
description: "string", // Optional
createdBy: {
"id": "uuid",
"name": "string", // Optional
"imageUrl": "string" // Optional
},
createdAt: "Date object",
parentAlbumId: "uuid",
liked: true,
likeCount: 42,
viewed: true,
viewCount: 42,
taggedMemberCount: 42,
visualMediaFields: {
extension: "string", //lowercase without dot "pdf", "jpg", etc.
size: "int",
previewImages: [
{
width: 200,
height: 400,
url: 'https://url.com',
status: 'READY|IN_PROGRESS'
}
]
},
albumFields: {
lastModified: "Date object"
}
}
Query Photo Share items
When no filters specified all items sorted by id
is returned until the limit is reached.
By item id:
const items = await photoSharingClient.getItemsByIds(
['3a992b33-97b7-413b-be11-bdbd65197fb9'],
{withProfiles: true, withRecentTaggedMembers: false}
);
Liked items:
const {items} = await photoSharingClient.getLikedItems('3a992b33-97b7-413b-be11-bdbd65197fb9', {});
Tagged items:
const {items} = await photoSharingClient.getTaggedItems('3a992b33-97b7-413b-be11-bdbd65197fb9', {});
Created items:
const {items} = await photoSharingClient.getCreatedItems('3a992b33-97b7-413b-be11-bdbd65197fb9', {});
All items in root album:
const {items} = await photoSharingClient.getItems({});
Generic query api:
const filter = {
// Only visual media liked by one of supplied users
likedBy: ["guid"],
// Only visual media one of supplied users tagged in
taggedBy: ["guid"],
// Only created/uploaded by given users
createdBy: ["guid"],
// Individual item ids. In this case pagination is not applicable.
// Results are ordered according to id order in request
itemIds: [],
// Item types. Only items of provided types are returned.
// If filter is not set, defaults to ['VISUAL_MEDIA']
itemTypes: ['ALBUM', 'VISUAL_MEDIA'],
// Only items contained in one of given albums. Empty returns everything flattened.
albumIds: []
};
const options = {
// Include name and imageUrl where member profile is fetched
withProfiles: true,
// Include recently tagged members
withRecentTaggedMembers: false,
sortBy: "MOST_RECENT|MOST_VIEWED|MOST_LIKED|MOST_COMMENTS",
orientation: "DESC|ASC",
limit: number, //Optional. Limited to max 100.
cursor: "string" //Optional. When unspecified will return first page until limit.
};
const {items, metadata} = await photoSharingClient.queryItems(filter, options);
// Use next_cursor to fetch additional page. null means no more pages.
// Filtering and sorting is encoded inside cursor.
// Cursor takes precedence over given `filter` and `sorting` parameters
const { nextCursor } = metadata;
Collect data api
- Enriches shared gallery items with additional data if requested
- Fetches more entities like settings, authorized actions in single request
const filter = {
likedBy: [],
taggedBy: [],
createdBy: [],
itemIds: [],
albumIds: [],
itemTypes: ["VISUAL_MEDIA", "ALBUM"]
};
const options = {
withProfiles: true,
withRecentTaggedMembers: false,
withSettings: true,
withAuthorizedActions: false,
sortBy: 'MOST_LIKED',
orientation: 'ASC',
limit: 100,
cursor: 'testCursor',
authorizedAlbumId: 'uuid'
};
const response = await photoSharingClient.collectData(filter, options);
Response example:
{
"items":[
{
"item":{
"id":"3a992b33-97b7-413b-be11-bdbd65197fb9",
"name":"test.png",
"description":"A very nice test file",
"createdBy":{
"id":"beffb997-e187-41b8-a736-4939255ee99e"
},
"createdAt": new Date("2019-01-03T09:50:34.000Z"),
"parentAlbumId":"3a992b33-97b7-413b-be11-bdbd65197fb9",
"likeCount":42,
"liked":true,
"viewCount":43,
"viewed":true,
"commentCount":42,
"type":"VISUAL_MEDIA",
"taggedMembersCount":0,
"recentTaggedMembers":[
],
"visualMediaFields":{
"extension":"png",
"size":{
"low":42000000,
"high":0,
"unsigned":false
},
"previewImages":[
{
"url":"http://preview.com",
"width":306,
"height":502,
"status":"IN_PROGRESS"
}
]
}
},
"recentComments":[
],
"reactions":[
],
"recentMemberTags":[
]
}
],
"metadata":{
"nextCursor":"testCursor"
},
"authorizedActions":[
],
"settings":{
"display":{
"enableViewsCounter":true,
"enableTagging":true,
"enableComments":true,
"defaultSort":"MOST_RECENT"
},
"upload":{
"allowVideoUploads":true
},
"layout":{
"layout":"MASONRY",
"spacing":64,
"galleryAlignment":"LEFT",
"photoSize":10
},
"design":{
"galleryButtonCornerRadius":0,
"photoItemBorderWidth":1,
"photoItemCornerRadius":0
},
"membersAreaDesign":{
"borderWidth":42,
"cornerRadius":41
}
}
}
Delete Photo Share item
Deletes visual media or album
const response = await photoSharingClient.delete(['3a992b33-97b7-413b-be11-bdbd65197fb9'], {parentAlbumId: '3a992b33-97b7-413b-be11-bdbd65197fb9'});
expect(response).to.equal(undefined);
Share item
Currently share is implemented as url shortener without any item/album context. It is responsible to prepend correct site domain to given path
const response = await photoSharingClient.share('url');
expect(response).to.deep.equal('http://wix.to/testing');
View Visual Media
const urls = await photoSharingClient.viewVisualMedia([{
itemId: '3a992b33-97b7-413b-be11-bdbd65197fb9',
incrementViewCount: true
}]);
expect(urls).to.deep.equal([{
image: {
width: 200,
height: 400
},
url: 'https://testviewurl.com',
itemId: '3a992b33-97b7-413b-be11-bdbd65197fb9'
}]);
Like
const response = await photoSharingClient.like('3a992b33-97b7-413b-be11-bdbd65197fb9', true);
expect(response).to.equal(undefined);
Download
Same endpoint can be used to download:
- single visual media item: by probiding single id
- multiple visual media items: by providing multiple visual media ids
- album: by providing folder id
- mixed: multiple albums and multiple visual media items
When download
is invoked with single visual media id it would return url that would download that media file.
Browser will start file download because url responds with header Content-Disposition: attachment
.
const response = await photoSharingClient.download(['3a992b33-97b7-413b-be11-bdbd65197fb9']);
expect(response).to.deep.equal({
isArchive: true,
url: 'https://downladurl.com'
});
Upload Visual Media
Visual media upload happens in 3 phases:
- start upload by getting upload url
- upload to upload url
- callback with upload response
You can initiate multiple media uploads with single request:
const response = await photoSharingClient.startVisualMediaUpload([{
name: 'fileName',
size: 123,
parentAlbumId: defaultAlbumId, // or other album id
actionId: 'clientgenerated'
}]);
expect(response.urls).to.deep.equal([{
actionId: 'actionId',
requestParameters: {
request: 'parameters'
},
url: 'uploadUrl'
}]);
expect(response.failures.length).to.equal(1);
const failure = response.failures[0];
expect(failure.actionId).to.equal('actionId');
expect(failure.fileName).to.equal('test.png');
expect(failure.visualMediaExtensionNotSupported).to.deep.equal({extension: 'jpg'});
expect(Long.fromValue(failure.visualMediaTooBig.maxSize)).to.deep.equal(Long.fromInt(43));
expect(Long.fromValue(failure.visualMediaTooBig.size)).to.deep.equal(Long.fromInt(42));
expect(failure.quotaExceeded).to.deep.equal({quota: 1, usage: 2, type: "VIDEO_DURATION"});
expect(failure.quotaExceeded).to.deep.equal({quota: 1, usage: 2, type: "STORAGE"});
You should not care what is in requestParameters
. Just map it to FormData
when performing upload.
Example of upload from browser:
<html>
<body>
<script>
const uploadUrl = 'urlFrom_startFileUpload';
const requestParameters = {}; // From startFileUpload
async function uploadFile(e) {
const file = e.elements[0].files[0];
console.log('fileName', file.nane); //This name should be provided to startFileUpload, completeFileUpload
//Populate form data with request parameters as is
const formData = new FormData(e);
Object.keys(requestParameters).forEach(key => formData.append(key, requestParameters[key]))
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData,
});
// this response must be provded to completeUpload
console.log(await response.text());
}
</script>
<form id="upload-form" enctype="multipart/form-data" action="" method="post" target="upload-result" onsubmit="uploadFile(this)">
<input id="file" name="file" type="file" accept="image/*">
<input id="submit" type="submit">
</form>
</body>
</html>
Complete upload by providing uploadResponse
:
const response = await photoSharingClient.completeVisualMediaUpload({
actions: [{
uploadResponse: 'from upload url',
albumId: defaultAlbumId, // or other album id
actionId: 'clientgenerated'
}]
});
// Responds with uploaded item metadata
expect(response.successes.length).to.equal(1);
const success = response.successes[0];
expect(success.actionId).to.equal('actionId');
// Might have validation failures
expect(response.failures.length).to.equal(1);
const failure = response.failures[0];
expect(failure.actionId).to.equal('actionId');
expect(failure.fileName).to.equal('test.png');
expect(Long.fromValue(failure.visualMediaTooBig.maxSize)).to.deep.equal(Long.fromInt(43));
expect(Long.fromValue(failure.visualMediaTooBig.size)).to.deep.equal(Long.fromInt(42));
expect(failure.quotaExceeded).to.deep.equal({quota: 1, usage: 2, type: "VIDEO_DURATION"});
expect(failure.quotaExceeded).to.deep.equal({quota: 1, usage: 2, type: "STORAGE"});
expect(failure.videoTooLong).to.deep.equal({duration: 1, limit: 2});
Complete upload responds with newly created library items. You can complete upload of multiple files with single request.
Set item description
const response = await photoSharingClient.setVisualMediaDescription(
'3a992b33-97b7-413b-be11-bdbd65197fb9',
'Some nice description'
);
expect(response).to.equal(undefined);
Create Album
const response = await photoSharingClient.createAlbum('Album Name');
expect(response.album.type).to.equal('ALBUM');
Example response
{"album": {
"id": "276101a9-8012-3b7b-be49-d3d966731a06",
"name": "Album Name",
"createdBy": {"id": "202495d7-6d78-49ec-b47b-f4f321cde0e9"},
"createdAt": "2020-06-01T10:33:55.113Z",
"parentAlbumId": "cb38d144-c9f8-499b-b5ac-b37541a2791b",
"likesCount": 0,
"isLiked": false,
"viewsCount": 0,
"isViewed": false,
"commentsCount": 0,
"type": "ALBUM",
"taggedMembersCount": 0,
"recentTaggedMembers": [],
"albumFields": {
"lastModified": "2020-06-01T10:34:00.995Z",
"childrenCount": 0
}
}}
Using the API directly: https://bo.wix.com/wix-docs/rest/social-groups/shared-gallery/shared-gallery-items/create-album
Update Album
const response = await photoSharingClient.updateAlbum('00000000-0000-0000-0000-000000000000',
{
name: 'New name',
description: 'Updated description',
coverImageId: '3a992b33-97b7-413b-be11-bdbd65197fb9',
wixMediaCoverImage: {
"id": "6b9457_f3644969218e4e5a89257485f6a88231~mv2.png",
"url": "media/6b9457_f3644969218e4e5a89257485f6a88231~mv2.png"
},
fieldMask: 'name,description,coverImageId,wixMediaCoverImage'
});
expect(response).to.be.deep.equal({});
This endpoint is going to change to support the new cover images implementation.
Using the API directly: https://bo.wix.com/wix-docs/rest/social-groups/shared-gallery/shared-gallery-items/update-album
Roles and Permissions
Authorized actions
Provides actions that are available to user per item (visual media or album).
Some actions like UPLOAD_VISUAL_MEDIA
means that this action is available in the context of provided id.
const response = await photoSharingClient.authorizeActions(['00000000-0000-0000-0000-000000000000'], {albumId: 'uuid'});
expect(response).to.deep.equal([
{
itemId: '00000000-0000-0000-0000-000000000000',
action: 'DOWNLOAD',
status: 'ALLOWED',
reason: 'DEFAULT_REASON'
},
{
itemId: '00000000-0000-0000-0000-000000000000',
action: 'UPLOAD_VISUAL_MEDIA',
status: 'FORBIDDEN',
reason: 'MUST_BE_A_MEMBER',
isAllowedForAllMembers: true
}
]);
Assign permissions
Assigning permission to all app (all visual media and all albums). Upload permission is only assignable per album.
const response = await photoSharingClient.assignPermissions([
{
permission: wix.sharedgallery.api.v1.permissions.Permission.UPLOAD_VISUAL_MEDIA,
roleId: '00000000-0000-0000-0000-000000000000'
}
]);
expect(response).to.be.deep.equal({});
Assigning permission to album.
const response = await photoSharingClient.assignPermissions([
{
permission: wix.sharedgallery.api.v1.permissions.Permission.UPLOAD_VISUAL_MEDIA,
roleId: '00000000-0000-0000-0000-000000000000',
albumId: '00000000-0000-0000-0000-000000000000'
}
]);
expect(response).to.be.deep.equal({});
Remove permissions
Remove permission from app.
const response = await photoSharingClient.removePermissions([
{
permission: wix.sharedgallery.api.v1.permissions.Permission.MODERATE,
roleId: '00000000-0000-0000-0000-000000000000'
}
]);
expect(response).to.be.deep.equal({});
Remove permission from album.
const response = await photoSharingClient.removePermissions([
{
permission: wix.sharedgallery.api.v1.permissions.Permission.MODERATE,
roleId: '00000000-0000-0000-0000-000000000000',
albumId: '00000000-0000-0000-0000-000000000000'
}
]);
expect(response).to.be.deep.equal({});
List roles with permission
const response = await photoSharingClient.listRoles({albumId: '00000000-0000-0000-0000-000000000000'});
expect(response).to.have.any.keys('roles');
When permissions
array is empty then role has no Photo Sharing permission assigned.
To fetch roles that have specific permission assigned use permissions
filter.
To fetch all roles omit permissions
filter (null or empty array).
const response = await photoSharingClient.listRoles({});
Role Entity
{
id: "uuid",
name: "user given name of a role",
permissions: [
'UPLOAD_VISUAL_MEDIA', 'MODERATE'
],
roleType: "ALL_MEMBERS|ADMINS|CUSTOM"
}
Current member
Note. This method will not work when doing CORS request from the browser.
await photoSharingClient.getCurrentMember();
{
"member": {
"id": "UUID",
"emailVerified": true,
"role": "OWNER",
"loginEmail": "[email protected]",
"memberName": "Vlad Opa",
"firstName": "Vlad",
"lastName": "Opa",
"imageUrl": "http://smth",
"nickname": "Vlad Opa Opa",
"profilePrivacyStatus": "PUBLIC",
"slug": "vlad",
"language": "en",
"status": "ACTIVE",
"creationDate": "2018-11-21T12:11:17.000Z",
"lastUpdateDate": "2019-03-21T07:13:16.750Z",
"lastLoginDate": "2019-05-03T14:15:34.000Z",
"emails": [],
"phones": [],
"addresses": [],
"labels": [],
"customFields": [],
"picture": {
"id": "",
"url": "http://smth",
"height": 0,
"width": 0
},
"userId": "UUID"
}
}
Get site member profile by nickname
const nicknameContains = 'test';
const offset = 5; // for initial page set 0
const limit = 100;
const membersThatContainNickname = photoSharingClient.querySiteMemberProfiles({
nicknameContains, // optional
offset, // optional defaults to 0
limit // optional defaults to 100
})
const first100MembersSortedByNameDesc = photoSharingClient.querySiteMemberProfiles({})
{
"profiles": [
{
"id": "202495d7-6d78-49ec-b47b-f4f321cde0e9",
"nickname": "Test Nickname",
"slug": "testnickname",
"imageUrl": "//static.wixstatic.com/media/202495_b668acc8853d4ca5a76081189c31099d~mv2.jpg_srz_320_320_75_22_0.5_1.20_0.00_jpg_srz",
"contactId": "6524caca-7bd6-4583-89d1-1fe5b1f97a13",
"userId": "202495d7-6d78-49ec-b47b-f4f321cde0e9",
"creationDate": "2018-12-18T15:41:20Z"
}
],
"pagination": {
"offset": 0,
"limit": 50,
"total": 6
}
};
Get site member profile by nickname (incl. tag status per item)
const membersThatContainNickname = photoSharingClient.queryMemberProfiles({
itemId: '3a992b33-97b7-413b-be11-bdbd65197fb9', //checks if member is tagged on item
nicknameContains: 'test'; // optional
nicknameContains,
paging: {
limit: 100, // optional defaults to 100
offset: 0 // optional defaults to 0
}
})
{
profiles: [
{
userId: '7623d216-a6d2-11e9-a2a3-2a2ae2dbcce4',
nickname: 'John Doe',
imageUrl: 'http://Test-image.png',
isTagged: true
}
],
paging: {
offset: 0,
limit: 100,
}
};
Get member tags
const response = await photoSharingClient.getMemberTags(
'3a992b33-97b7-413b-be11-bdbd65197fb9',
{limit: 100, withProfiles: true, cursor: 'cursor'}
);
expect(response).to.deep.equal({
cursor: 'cursor',
itemId: '3a992b33-97b7-413b-be11-bdbd65197fb9',
tags: [{
canRemove: true,
memberId: '00000000-0000-0000-0000-000000000000',
profile: {
id: '00000000-0000-0000-0000-000000000000',
imageUrl: 'http://profile.pic',
name: 'test name'
}
}]
});
Tag members
const update = {
memberId: '00000000-0000-0000-0000-000000000000',
isTagged: true //true to set tag, false to remove tag
};
const response = await photoSharingClient.tagMembers('3a992b33-97b7-413b-be11-bdbd65197fb9', [update]);
expect(response).to.equal(undefined);
Report item
Requires appDefId
of reporting application.
true
means item was reported.
const options = {appDefId: '00000000-0000-0000-0000-000000000000'};
const response = await photoSharingClient.report(
'3a992b33-97b7-413b-be11-bdbd65197fb9',
wix.sharedgallery.api.v1.items.ReportReason.UNWANTED_OR_SPAM,
options
);
expect(response).to.equal(true);
Don't forget to check if REPORT
action is available in authorizeActions
api.
Create album
Used to create albums which later can be retrieved with generic query api
const response = await photoSharingClient.createAlbum('Album Name');
Update album
Used to update album's fields. You only need to pass fields you want to update. Cover image must be a visual media item inside the album.
//All fields update
const response = await photoSharingClient.updateAlbum(
'00000000-0000-0000-0000-000000000000',
{
name: 'New name',
description: 'Updated description',
coverImageId: '3a992b33-97b7-413b-be11-bdbd65197fb9'
}
);
//Album description update
const response = await photoSharingClient.updateAlbum(
'00000000-0000-0000-0000-000000000000',
{
description: 'Updated description'
}
);