parent
00b1c6ef6f
commit
8eceab9f3d
@ -0,0 +1,31 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* postContext: JSONLD Context for post |
||||
* ============================================================================= |
||||
*/ |
||||
export const postContext: ContextDefinition = { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
SocialMediaPosting: "http://schema.org/SocialMediaPosting", |
||||
CreativeWork: "http://schema.org/CreativeWork", |
||||
Thing: "http://schema.org/Thing", |
||||
articleBody: { |
||||
"@id": "http://schema.org/articleBody", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
uploadDate: { |
||||
"@id": "http://schema.org/uploadDate", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#date", |
||||
}, |
||||
image: { |
||||
"@id": "http://schema.org/image", |
||||
"@type": "@id", |
||||
}, |
||||
publisher: { |
||||
"@id": "http://schema.org/publisher", |
||||
"@type": "@id", |
||||
}, |
||||
}; |
@ -0,0 +1,155 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* postSchema: ShexJ Schema for post |
||||
* ============================================================================= |
||||
*/ |
||||
export const postSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "https://example.com/PostSh", |
||||
type: "ShapeDecl", |
||||
shapeExpr: { |
||||
type: "Shape", |
||||
expression: { |
||||
type: "EachOf", |
||||
expressions: [ |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: [ |
||||
"http://schema.org/SocialMediaPosting", |
||||
"http://schema.org/CreativeWork", |
||||
"http://schema.org/Thing", |
||||
], |
||||
}, |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/articleBody", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "articleBody", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The actual body of the article. ", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/uploadDate", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#date", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "uploadDate", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"Date when this media object was uploaded to this site.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/image", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "image", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"A media object that encodes this CreativeWork. This property is a synonym for encoding.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://schema.org/publisher", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "publisher", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The publisher of the creative work.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#label", |
||||
object: { |
||||
value: "SocialMediaPost", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"A post to a social media platform, including blog posts, tweets, Facebook posts, etc.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,19 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { postSchema } from "./post.schema"; |
||||
import { postContext } from "./post.context"; |
||||
import { PostSh } from "./post.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes post |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* PostSh ShapeType |
||||
*/ |
||||
export const PostShShapeType: ShapeType<PostSh> = { |
||||
schema: postSchema, |
||||
shape: "https://example.com/PostSh", |
||||
context: postContext, |
||||
}; |
@ -0,0 +1,45 @@ |
||||
import { ContextDefinition } from "jsonld"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for post |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* PostSh Type |
||||
*/ |
||||
export interface PostSh { |
||||
"@id"?: string; |
||||
"@context"?: ContextDefinition; |
||||
type: |
||||
| { |
||||
"@id": "SocialMediaPosting"; |
||||
} |
||||
| { |
||||
"@id": "CreativeWork"; |
||||
} |
||||
| { |
||||
"@id": "Thing"; |
||||
}; |
||||
/** |
||||
* The actual body of the article. |
||||
*/ |
||||
articleBody?: string; |
||||
/** |
||||
* Date when this media object was uploaded to this site. |
||||
*/ |
||||
uploadDate: string; |
||||
/** |
||||
* A media object that encodes this CreativeWork. This property is a synonym for encoding. |
||||
*/ |
||||
image?: { |
||||
"@id": string; |
||||
}; |
||||
/** |
||||
* The publisher of the creative work. |
||||
*/ |
||||
publisher: { |
||||
"@id": string; |
||||
}; |
||||
} |
@ -0,0 +1,23 @@ |
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||
PREFIX ex: <https://example.com/> |
||||
BASE <http://schema.org/> |
||||
|
||||
ex:PostSh { |
||||
a [<SocialMediaPosting> <CreativeWork> <Thing>] ; |
||||
<articleBody> xsd:string? |
||||
// rdfs:label '''articleBody''' |
||||
// rdfs:comment '''The actual body of the article. ''' ; |
||||
<uploadDate> xsd:date |
||||
// rdfs:label '''uploadDate''' |
||||
// rdfs:comment '''Date when this media object was uploaded to this site.''' ; |
||||
<image> IRI ? |
||||
// rdfs:label '''image''' |
||||
// rdfs:comment '''A media object that encodes this CreativeWork. This property is a synonym for encoding.''' ; |
||||
<publisher> IRI |
||||
// rdfs:label '''publisher''' |
||||
// rdfs:comment '''The publisher of the creative work.''' ; |
||||
} |
||||
// rdfs:label '''SocialMediaPost''' |
||||
// rdfs:comment '''A post to a social media platform, including blog posts, tweets, Facebook posts, etc.''' |
@ -1,10 +1,91 @@ |
||||
import React, { useCallback } from "react"; |
||||
import type { FunctionComponent } from "react"; |
||||
import React, { useCallback, useState, useRef } from "react"; |
||||
import type { FunctionComponent, FormEvent } from "react"; |
||||
import type { Container, Leaf } from "@ldo/solid"; |
||||
import { v4 } from "uuid"; |
||||
import { useLdo, useSolidAuth } from "@ldo/solid-react"; |
||||
import { PostShShapeType } from "../.ldo/post.shapeTypes"; |
||||
|
||||
export const UploadButton: FunctionComponent = () => { |
||||
const upload = useCallback(() => { |
||||
const _message = prompt("Type a message for your post"); |
||||
}, []); |
||||
export const UploadButton: FunctionComponent<{ mainContainer: Container }> = ({ |
||||
mainContainer, |
||||
}) => { |
||||
const [message, setMessage] = useState(""); |
||||
const [selectedFile, setSelectedFile] = useState<File | undefined>(); |
||||
const fileInputRef = useRef<HTMLInputElement | null>(null); |
||||
const { createData, commitData } = useLdo(); |
||||
const { session } = useSolidAuth(); |
||||
const onSubmit = useCallback( |
||||
async (e: FormEvent<HTMLFormElement>) => { |
||||
e.preventDefault(); |
||||
|
||||
return <button onClick={upload}>Make a New Post</button>; |
||||
// Create the container file
|
||||
const mediaContainer = await mainContainer.createChildAndOverwrite( |
||||
`${v4()}/`, |
||||
); |
||||
if (mediaContainer.type === "error") { |
||||
alert(mediaContainer.message); |
||||
return; |
||||
} |
||||
|
||||
// Upload Image
|
||||
let uploadedImage: Leaf | undefined; |
||||
if (selectedFile) { |
||||
const result = await mediaContainer.uploadChildAndOverwrite( |
||||
selectedFile.name, |
||||
selectedFile, |
||||
selectedFile.type, |
||||
); |
||||
if (result.type === "error") { |
||||
alert(result.message); |
||||
await mediaContainer.delete(); |
||||
return; |
||||
} |
||||
uploadedImage = result; |
||||
} |
||||
|
||||
// Create Post
|
||||
const indexResource = mediaContainer.child("index.ttl"); |
||||
const post = createData( |
||||
PostShShapeType, |
||||
indexResource.uri, |
||||
indexResource, |
||||
); |
||||
post.articleBody = message; |
||||
if (uploadedImage) { |
||||
post.image = { "@id": uploadedImage.uri }; |
||||
} |
||||
if (session.webId) { |
||||
post.publisher = { "@id": session.webId }; |
||||
} |
||||
post.type = { "@id": "SocialMediaPosting" }; |
||||
post.uploadDate = new Date().toISOString(); |
||||
const result = await commitData(post); |
||||
if (result.type === "error") { |
||||
alert(result.message); |
||||
} |
||||
|
||||
// Clear the UI after Upload
|
||||
setMessage(""); |
||||
setSelectedFile(undefined); |
||||
if (fileInputRef.current) fileInputRef.current.value = ""; |
||||
}, |
||||
[message, selectedFile, session.webId], |
||||
); |
||||
|
||||
return ( |
||||
<form onSubmit={onSubmit}> |
||||
<input |
||||
type="text" |
||||
placeholder="Make a Post" |
||||
value={message} |
||||
onChange={(e) => setMessage(e.target.value)} |
||||
/> |
||||
<input |
||||
type="file" |
||||
accept="image/*" |
||||
ref={fileInputRef} |
||||
onChange={(e) => setSelectedFile(e.target.files?.[0])} |
||||
/> |
||||
<input type="submit" value="Post" /> |
||||
</form> |
||||
); |
||||
}; |
||||
|
@ -0,0 +1,91 @@ |
||||
import type { LdoBase, ShapeType } from "@ldo/ldo"; |
||||
import { transactionChanges } from "@ldo/ldo"; |
||||
import { write } from "@ldo/ldo"; |
||||
import { startTransaction } from "@ldo/ldo"; |
||||
import type { SubjectNode } from "@ldo/rdf-utils"; |
||||
import type { Resource, SolidLdoDataset } from "@ldo/solid"; |
||||
|
||||
export interface UseLdoMethods { |
||||
dataset: SolidLdoDataset; |
||||
getResource: SolidLdoDataset["getResource"]; |
||||
getSubject<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
subject: string | SubjectNode, |
||||
): Type | Error; |
||||
createData<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
subject: string | SubjectNode, |
||||
...resources: Resource[] |
||||
): Type; |
||||
changeData<Type extends LdoBase>(input: Type, ...resources: Resource[]): Type; |
||||
commitData(input: LdoBase): ReturnType<SolidLdoDataset["commitChangesToPod"]>; |
||||
} |
||||
|
||||
export function createUseLdoMethods(dataset: SolidLdoDataset): UseLdoMethods { |
||||
return { |
||||
dataset: dataset, |
||||
/** |
||||
* Gets a resource |
||||
*/ |
||||
getResource: dataset.getResource.bind(dataset), |
||||
/** |
||||
* Returns a Linked Data Object for a subject |
||||
* @param shapeType The shape type for the data |
||||
* @param subject Subject Node |
||||
* @returns A Linked Data Object |
||||
*/ |
||||
getSubject<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
subject: string | SubjectNode, |
||||
): Type | Error { |
||||
return dataset.usingType(shapeType).fromSubject(subject); |
||||
}, |
||||
/** |
||||
* Begins tracking changes to eventually commit for a new subject |
||||
* @param shapeType The shape type that defines the created data |
||||
* @param subject The RDF subject for a Linked Data Object |
||||
* @param resources Any number of resources to which this data should be written |
||||
* @returns A Linked Data Object to modify and commit |
||||
*/ |
||||
createData<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
subject: string | SubjectNode, |
||||
...resources: Resource[] |
||||
): Type { |
||||
const linkedDataObject = dataset |
||||
.usingType(shapeType) |
||||
.write(...resources.map((r) => r.uri)) |
||||
.fromSubject(subject); |
||||
startTransaction(linkedDataObject); |
||||
return linkedDataObject; |
||||
}, |
||||
/** |
||||
* Begins tracking changes to eventually commit |
||||
* @param input A linked data object to track changes on |
||||
* @param resources |
||||
*/ |
||||
changeData<Type extends LdoBase>( |
||||
input: Type, |
||||
...resources: Resource[] |
||||
): Type { |
||||
// Clone the input and set a graph
|
||||
const [transactionLdo] = write(...resources.map((r) => r.uri)).usingCopy( |
||||
input, |
||||
); |
||||
// Start a transaction with the input
|
||||
startTransaction(transactionLdo); |
||||
// Return
|
||||
return transactionLdo; |
||||
}, |
||||
/** |
||||
* Commits the transaction to the global dataset, syncing all subscribing |
||||
* components and Solid Pods |
||||
*/ |
||||
commitData( |
||||
input: LdoBase, |
||||
): ReturnType<SolidLdoDataset["commitChangesToPod"]> { |
||||
const changes = transactionChanges(input); |
||||
return dataset.commitChangesToPod(changes); |
||||
}, |
||||
}; |
||||
} |
@ -0,0 +1,57 @@ |
||||
import type { SubjectNode } from "@ldo/rdf-utils"; |
||||
import { |
||||
ContextUtil, |
||||
JsonldDatasetProxyBuilder, |
||||
} from "@ldo/jsonld-dataset-proxy"; |
||||
import type { ShapeType } from "@ldo/ldo"; |
||||
import { LdoBuilder } from "@ldo/ldo"; |
||||
import type { LdoBase } from "@ldo/ldo"; |
||||
import { useLdo } from "./SolidLdoProvider"; |
||||
import { useCallback, useEffect, useMemo, useState } from "react"; |
||||
import { TrackingProxyContext } from "./util/TrackingProxyContext"; |
||||
import { defaultGraph } from "@rdfjs/data-model"; |
||||
|
||||
export function useSubject<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
subject: string | SubjectNode, |
||||
): Type { |
||||
const { dataset } = useLdo(); |
||||
|
||||
const [forceUpdateCounter, setForceUpdateCounter] = useState(0); |
||||
const forceUpdate = useCallback( |
||||
() => setForceUpdateCounter((val) => val + 1), |
||||
[], |
||||
); |
||||
|
||||
// The main linked data object
|
||||
const linkedDataObject = useMemo(() => { |
||||
// Remove all current subscriptions
|
||||
dataset.removeListenerFromAllEvents(forceUpdate); |
||||
|
||||
// Rebuild the LdoBuilder from scratch to inject TrackingProxyContext
|
||||
const contextUtil = new ContextUtil(shapeType.context); |
||||
const proxyContext = new TrackingProxyContext( |
||||
{ |
||||
dataset, |
||||
contextUtil, |
||||
writeGraphs: [defaultGraph()], |
||||
languageOrdering: ["none", "en", "other"], |
||||
}, |
||||
forceUpdate, |
||||
); |
||||
const builder = new LdoBuilder( |
||||
new JsonldDatasetProxyBuilder(proxyContext), |
||||
shapeType, |
||||
); |
||||
return builder.fromSubject(subject); |
||||
}, [shapeType, subject, dataset, forceUpdateCounter, forceUpdate]); |
||||
|
||||
useEffect(() => { |
||||
// Unregister force update listener upon unmount
|
||||
return () => { |
||||
dataset.removeListenerFromAllEvents(forceUpdate); |
||||
}; |
||||
}, [shapeType, subject]); |
||||
|
||||
return linkedDataObject; |
||||
} |
@ -1,3 +1,42 @@ |
||||
import type { DatasetChanges } from "@ldo/rdf-utils"; |
||||
import { mergeDatasetChanges } from "@ldo/subscribable-dataset"; |
||||
import type { Quad } from "@rdfjs/types"; |
||||
import { Requester } from "./Requester"; |
||||
import type { UpdateResult } from "./requests/updateDataResource"; |
||||
import { updateDataResource } from "./requests/updateDataResource"; |
||||
|
||||
export class LeafRequester extends Requester {} |
||||
export const UPDATE_KEY = "update"; |
||||
|
||||
export class LeafRequester extends Requester { |
||||
isUpdating(): boolean { |
||||
return this.requestBatcher.isLoading(UPDATE_KEY); |
||||
} |
||||
|
||||
/** |
||||
* Update the data on this resource |
||||
* @param changes |
||||
*/ |
||||
async updateDataResource( |
||||
changes: DatasetChanges<Quad>, |
||||
): Promise<UpdateResult> { |
||||
const result = await this.requestBatcher.queueProcess({ |
||||
name: UPDATE_KEY, |
||||
args: [ |
||||
{ uri: this.uri, fetch: this.context.fetch }, |
||||
changes, |
||||
this.context.solidLdoDataset, |
||||
], |
||||
perform: updateDataResource, |
||||
modifyQueue: (queue, isLoading, [, changes]) => { |
||||
if (queue[queue.length - 1].name === UPDATE_KEY) { |
||||
// Merge Changes
|
||||
const originalChanges = queue[queue.length - 1].args[1]; |
||||
mergeDatasetChanges(originalChanges, changes); |
||||
return true; |
||||
} |
||||
return false; |
||||
}, |
||||
}); |
||||
return result; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,12 @@ |
||||
import type { Container } from "../../resource/Container"; |
||||
import type { Leaf } from "../../resource/Leaf"; |
||||
import { RequesterResult } from "./RequesterResult"; |
||||
|
||||
export class CommitChangesSuccess extends RequesterResult { |
||||
readonly type = "commitChangesSuccess" as const; |
||||
readonly affectedResources: (Leaf | Container)[]; |
||||
constructor(uri: string, affectedResources: (Leaf | Container)[]) { |
||||
super(uri); |
||||
this.affectedResources = affectedResources; |
||||
} |
||||
} |
@ -1,15 +1,45 @@ |
||||
import type { DatasetChanges } from "@ldo/rdf-utils"; |
||||
import type { DataResult } from "../requestResults/DataResult"; |
||||
import { changesToSparqlUpdate } from "@ldo/rdf-utils"; |
||||
import { DataResult } from "../requestResults/DataResult"; |
||||
import type { HttpErrorResultType } from "../requestResults/HttpErrorResult"; |
||||
import type { UnexpectedError } from "../requestResults/ErrorResult"; |
||||
import type { RequestParams } from "./requestParams"; |
||||
import { HttpErrorResult } from "../requestResults/HttpErrorResult"; |
||||
import { UnexpectedError } from "../requestResults/ErrorResult"; |
||||
import type { SimpleRequestParams } from "./requestParams"; |
||||
import type { SubscribableDataset } from "@ldo/subscribable-dataset"; |
||||
import type { Quad } from "@rdfjs/types"; |
||||
|
||||
export type UpdateResult = DataResult | UpdateResultError; |
||||
export type UpdateResultError = HttpErrorResultType | UnexpectedError; |
||||
|
||||
export async function updateDataResource( |
||||
_params: RequestParams, |
||||
_datasetChanges: DatasetChanges, |
||||
{ uri, fetch }: SimpleRequestParams, |
||||
datasetChanges: DatasetChanges<Quad>, |
||||
mainDataset: SubscribableDataset<Quad>, |
||||
): Promise<UpdateResult> { |
||||
throw new Error("Not Implemented"); |
||||
try { |
||||
// Put Changes in transactional dataset
|
||||
const transaction = mainDataset.startTransaction(); |
||||
transaction.addAll(datasetChanges.added || []); |
||||
datasetChanges.removed?.forEach((quad) => transaction.delete(quad)); |
||||
// Commit data optimistically
|
||||
transaction.commit(); |
||||
// Make request
|
||||
const sparqlUpdate = await changesToSparqlUpdate(datasetChanges); |
||||
const response = await fetch(uri, { |
||||
method: "PATCH", |
||||
body: sparqlUpdate, |
||||
headers: { |
||||
"Content-Type": "application/sparql-update", |
||||
}, |
||||
}); |
||||
const httpError = HttpErrorResult.checkResponse(uri, response); |
||||
if (httpError) { |
||||
// Handle error rollback
|
||||
transaction.rollback(); |
||||
return httpError; |
||||
} |
||||
return new DataResult(uri); |
||||
} catch (err) { |
||||
return UnexpectedError.fromThrown(uri, err); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,53 @@ |
||||
import type { DatasetChanges } from "@ldo/rdf-utils"; |
||||
import type { BaseQuad } from "@rdfjs/types"; |
||||
|
||||
/** |
||||
* Merges a new change into an original change |
||||
* @param originalChange |
||||
* @param newChange |
||||
*/ |
||||
export function mergeDatasetChanges<InAndOutQuad extends BaseQuad>( |
||||
originalChange: DatasetChanges<InAndOutQuad>, |
||||
newChange: DatasetChanges<InAndOutQuad>, |
||||
): void { |
||||
// Add added
|
||||
if (newChange.added) { |
||||
if (originalChange.added) { |
||||
originalChange.added.addAll(newChange.added); |
||||
} else { |
||||
originalChange.added = newChange.added; |
||||
} |
||||
// Delete from removed if present
|
||||
const changesIntersection = originalChange.removed?.intersection( |
||||
newChange.added, |
||||
); |
||||
if (changesIntersection && changesIntersection.size > 0) { |
||||
originalChange.removed = |
||||
originalChange.removed?.difference(changesIntersection); |
||||
} |
||||
} |
||||
// Add removed
|
||||
if (newChange.removed) { |
||||
if (originalChange.removed) { |
||||
originalChange.removed.addAll(newChange.removed); |
||||
} else { |
||||
originalChange.removed = newChange.removed; |
||||
} |
||||
// Delete from added if present
|
||||
const changesIntersection = originalChange.added?.intersection( |
||||
newChange.removed, |
||||
); |
||||
if (changesIntersection && changesIntersection.size > 0) { |
||||
originalChange.added = |
||||
originalChange.added?.difference(changesIntersection); |
||||
} |
||||
} |
||||
|
||||
// Make undefined if size is zero
|
||||
if (originalChange.added && originalChange.added.size === 0) { |
||||
originalChange.added = undefined; |
||||
} |
||||
if (originalChange.removed && originalChange.removed.size === 0) { |
||||
originalChange.removed = undefined; |
||||
} |
||||
} |
Loading…
Reference in new issue