-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Double vendor toggles #4189
Merged
Merged
Double vendor toggles #4189
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e2277b6
Change type of EnabledIds
allisonking e6d2370
Start supporting both consent and legint vendors
allisonking a73c13d
Store vendor consents in TC string and read from it
allisonking 658620f
Small fixes
allisonking 46c8b4c
Fix if placement
allisonking b661139
Merge branch 'main' into aking/4175/vendor-toggles
allisonking 076fe7e
Update comments
allisonking be5a127
Update changelog
allisonking File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { h, FunctionComponent } from "preact"; | ||
import { useState, useCallback, useMemo } from "preact/hooks"; | ||
import { TCString } from "@iabtechlabtcf/core"; | ||
import ConsentBanner from "../ConsentBanner"; | ||
|
||
import { | ||
|
@@ -14,23 +15,26 @@ import Overlay from "../Overlay"; | |
import { TcfConsentButtons } from "./TcfConsentButtons"; | ||
import { OverlayProps } from "../types"; | ||
|
||
import type { | ||
TCFFeatureRecord, | ||
TCFFeatureSave, | ||
TCFPurposeRecord, | ||
TCFPurposeSave, | ||
TCFSpecialFeatureSave, | ||
TCFSpecialPurposeSave, | ||
TCFVendorRecord, | ||
TCFVendorSave, | ||
TcfSavePreferences, | ||
import { | ||
type EnabledIds, | ||
LegalBasisForProcessingEnum, | ||
type TCFFeatureRecord, | ||
type TCFFeatureSave, | ||
type TCFPurposeRecord, | ||
type TCFPurposeSave, | ||
type TCFSpecialFeatureSave, | ||
type TCFSpecialPurposeSave, | ||
type TCFVendorRecord, | ||
type TCFVendorSave, | ||
type TcfSavePreferences, | ||
} from "../../lib/tcf/types"; | ||
|
||
import { updateConsentPreferences } from "../../lib/preferences"; | ||
import { | ||
ButtonType, | ||
ConsentMethod, | ||
PrivacyExperience, | ||
UserConsentPreference, | ||
} from "../../lib/consent-types"; | ||
import { generateTcString } from "../../lib/tcf"; | ||
import { | ||
|
@@ -41,6 +45,7 @@ import InitialLayer from "./InitialLayer"; | |
import TcfTabs from "./TcfTabs"; | ||
import Button from "../Button"; | ||
import VendorInfoBanner from "./VendorInfoBanner"; | ||
import { vendorRecordsWithLegalBasis } from "../../lib/tcf/vendors"; | ||
|
||
const resolveConsentValueFromTcfModel = ( | ||
model: TCFPurposeRecord | TCFFeatureRecord | TCFVendorRecord | ||
|
@@ -78,14 +83,24 @@ const getEnabledIds = (modelList: TcfModels) => { | |
.map((model) => `${model.id}`); | ||
}; | ||
|
||
export interface EnabledIds { | ||
purposes: string[]; | ||
specialPurposes: string[]; | ||
features: string[]; | ||
specialFeatures: string[]; | ||
vendors: string[]; | ||
systems: string[]; | ||
} | ||
const getVendorEnabledIds = ( | ||
modelList: TCFVendorRecord[] | undefined, | ||
legalBasis: LegalBasisForProcessingEnum | ||
) => { | ||
if (!modelList) { | ||
return []; | ||
} | ||
const records = vendorRecordsWithLegalBasis(modelList, legalBasis); | ||
if (legalBasis === LegalBasisForProcessingEnum.LEGITIMATE_INTERESTS) { | ||
// TODO: the backend should eventually return legint fields with a default preference of OPT_IN | ||
allisonking marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const modifiedRecords = records.map((record) => ({ | ||
...record, | ||
default_preference: UserConsentPreference.OPT_IN, | ||
})); | ||
return getEnabledIds(modifiedRecords); | ||
} | ||
return getEnabledIds(records); | ||
}; | ||
|
||
export interface UpdateEnabledIds { | ||
newEnabledIds: string[]; | ||
|
@@ -119,32 +134,67 @@ const createTcfSavePayload = ({ | |
}: { | ||
experience: PrivacyExperience; | ||
enabledIds: EnabledIds; | ||
}): TcfSavePreferences => ({ | ||
purpose_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_purposes, | ||
enabledIds: enabledIds.purposes, | ||
}) as TCFPurposeSave[], | ||
special_feature_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_special_features, | ||
enabledIds: enabledIds.specialFeatures, | ||
}) as TCFSpecialFeatureSave[], | ||
vendor_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_vendors, | ||
enabledIds: enabledIds.vendors, | ||
}) as TCFVendorSave[], | ||
system_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_systems, | ||
enabledIds: enabledIds.systems, | ||
}) as TCFVendorSave[], | ||
}); | ||
}): TcfSavePreferences => { | ||
// Because systems were combined with vendors to make the UI easier to work with, | ||
// we need to separate them out now (the backend treats them as separate entities). | ||
const systemIds = experience.tcf_systems | ||
? experience.tcf_systems.map((s) => s.id) | ||
: []; | ||
const enabledSystemIds: string[] = []; | ||
const enabledVendorIds: string[] = []; | ||
enabledIds.vendorsConsent.forEach((id) => { | ||
if (systemIds.includes(id)) { | ||
enabledSystemIds.push(id); | ||
} else { | ||
enabledVendorIds.push(id); | ||
} | ||
}); | ||
enabledIds.vendorsLegint.forEach((id) => { | ||
if (systemIds.includes(id)) { | ||
enabledSystemIds.push(id); | ||
} else { | ||
enabledVendorIds.push(id); | ||
} | ||
}); | ||
|
||
return { | ||
purpose_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_purposes, | ||
enabledIds: enabledIds.purposes, | ||
}) as TCFPurposeSave[], | ||
special_feature_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_special_features, | ||
enabledIds: enabledIds.specialFeatures, | ||
}) as TCFSpecialFeatureSave[], | ||
vendor_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_vendors, | ||
// TODO: once the backend is storing this, we should send vendorsConsent | ||
// and vendorsLegint to separate fields (fidesplus1128) | ||
enabledIds: enabledVendorIds, | ||
}) as TCFVendorSave[], | ||
system_preferences: transformTcfModelToTcfSave({ | ||
modelList: experience.tcf_systems, | ||
enabledIds: enabledSystemIds, | ||
}) as TCFVendorSave[], | ||
}; | ||
}; | ||
|
||
const updateCookie = async ( | ||
oldCookie: FidesCookie, | ||
/** | ||
* `tcf` and `enabledIds` should represent the same data, where `tcf` is what is | ||
* sent to the backend, and `enabledIds` is what the FE uses. They have diverged | ||
* because the backend has not implemented separate vendor legint/consents yet. | ||
* Therefore, we need both entities right now, but eventually we should be able to | ||
* only use one. In other words, `enabledIds` has a field for `vendorsConsent` and | ||
* `vendorsLegint` but `tcf` only has `vendors`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for this code comment, it helps me understand approach here! |
||
*/ | ||
tcf: TcfSavePreferences, | ||
enabledIds: EnabledIds, | ||
experience: PrivacyExperience | ||
): Promise<FidesCookie> => { | ||
const tcString = await generateTcString({ | ||
tcStringPreferences: tcf, | ||
tcStringPreferences: enabledIds, | ||
experience, | ||
}); | ||
return { | ||
|
@@ -170,15 +220,55 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({ | |
tcf_systems: systems, | ||
} = experience; | ||
|
||
const vendorsAndSystems = [...(vendors || []), ...(systems || [])]; | ||
let vendorsConsent = getVendorEnabledIds( | ||
vendorsAndSystems, | ||
LegalBasisForProcessingEnum.CONSENT | ||
); | ||
let vendorsLegint = getVendorEnabledIds( | ||
vendorsAndSystems, | ||
LegalBasisForProcessingEnum.LEGITIMATE_INTERESTS | ||
); | ||
|
||
// Initialize vendor values from the TC string if it's available. Neither the | ||
// backend nor the cookie store vendorsConsent or vendorsLegint yet, so we must | ||
// look at the string. (fidesplus#1128) | ||
if (cookie.tc_string && cookie.tc_string !== "") { | ||
const tcModel = TCString.decode(cookie.tc_string || ""); | ||
vendorsConsent = []; | ||
vendorsLegint = []; | ||
tcModel.vendorConsents.forEach((consented, id) => { | ||
if (consented) { | ||
vendorsConsent.push(`${id}`); | ||
} | ||
}); | ||
tcModel.vendorLegitimateInterests.forEach((consented, id) => { | ||
if (consented) { | ||
vendorsLegint.push(`${id}`); | ||
} | ||
}); | ||
// but we still need to join system data to this | ||
const systemConsents = getVendorEnabledIds( | ||
systems, | ||
LegalBasisForProcessingEnum.CONSENT | ||
); | ||
const systemLegints = getVendorEnabledIds( | ||
systems, | ||
LegalBasisForProcessingEnum.LEGITIMATE_INTERESTS | ||
); | ||
vendorsConsent = [...vendorsConsent, ...systemConsents]; | ||
vendorsLegint = [...vendorsLegint, ...systemLegints]; | ||
} | ||
|
||
return { | ||
purposes: getEnabledIds(purposes), | ||
specialPurposes: getEnabledIds(specialPurposes), | ||
features: getEnabledIds(features), | ||
specialFeatures: getEnabledIds(specialFeatures), | ||
vendors: getEnabledIds(vendors), | ||
systems: getEnabledIds(systems), | ||
vendorsConsent, | ||
vendorsLegint, | ||
}; | ||
}, [experience]); | ||
}, [experience, cookie]); | ||
|
||
const [draftIds, setDraftIds] = useState<EnabledIds>(initialEnabledIds); | ||
|
||
|
@@ -208,7 +298,8 @@ const TcfOverlay: FunctionComponent<OverlayProps> = ({ | |
debug: options.debug, | ||
servedNotices: null, // TODO: served notices | ||
tcf, | ||
updateCookie: (oldCookie) => updateCookie(oldCookie, tcf, experience), | ||
updateCookie: (oldCookie) => | ||
updateCookie(oldCookie, tcf, enabledIds, experience), | ||
}); | ||
setDraftIds(enabledIds); | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not a good pattern! it's weird that there'd be a toggle defined in the component, and a separate one passed in. but it works quite well, and time is short 🥲
eventually, I think we should totally rework
TcfVendors
to be a table, and maybe not even useDataUseToggle
which was never really meant to be a table.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow up ticket: #4190