-
Notifications
You must be signed in to change notification settings - Fork 5k
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
refactor: convert icon-factory.js
to typescript
#23823
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
34c18e2
refactor: move icon-factory
davidmurdoch 08b602a
refactor: convert icon-factory.js to TypeScript
davidmurdoch 18f5a6f
Merge branch 'develop' into typescript-icon-factory
davidmurdoch f4bfed4
Merge branch 'develop' into typescript-icon-factory
HowardBraham 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 was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; | ||
|
||
/** | ||
* Defines the metadata for a token including optional icon URL. | ||
*/ | ||
type TokenMetadata = { | ||
iconUrl: string; | ||
}; | ||
|
||
/** | ||
* A factory for generating icons for cryptocurrency addresses using Jazzicon or predefined token metadata. | ||
*/ | ||
class IconFactory { | ||
/** | ||
* Function to generate a Jazzicon SVG element. | ||
*/ | ||
jazzicon: (diameter: number, seed: number) => SVGSVGElement; | ||
|
||
/** | ||
* Cache for storing generated SVG elements to avoid re-rendering. | ||
*/ | ||
cache: { [key: string]: SVGSVGElement }; | ||
|
||
/** | ||
* Constructs an IconFactory instance with a given Jazzicon function. | ||
* | ||
* @param jazzicon - A function that returns a Jazzicon SVG given a diameter and seed. | ||
*/ | ||
constructor(jazzicon: (diameter: number, seed: number) => SVGSVGElement) { | ||
this.jazzicon = jazzicon; | ||
this.cache = {}; | ||
} | ||
|
||
/** | ||
* Generates an icon for a given address. Returns a predefined image or generates a new Jazzicon. | ||
* | ||
* @param address - The cryptocurrency address to generate the icon for. | ||
* @param diameter - The diameter of the icon to be generated. | ||
* @param tokenMetadata - Metadata containing optional icon URL for predefined icons. | ||
* @returns An HTML element representing the icon. | ||
*/ | ||
iconForAddress( | ||
address: string, | ||
diameter: number, | ||
tokenMetadata?: Partial<TokenMetadata>, | ||
): HTMLElement | SVGSVGElement { | ||
if (iconExistsFor(address, tokenMetadata)) { | ||
return imageElFor(tokenMetadata); | ||
} | ||
|
||
return this.generateIdenticonSvg(address, diameter); | ||
} | ||
|
||
/** | ||
* Generates or retrieves from cache a Jazzicon SVG for a given address and diameter. | ||
* | ||
* @param address - The cryptocurrency address for the identicon. | ||
* @param diameter - The diameter of the identicon. | ||
* @returns A Jazzicon SVG element. | ||
*/ | ||
generateIdenticonSvg(address: string, diameter: number): SVGSVGElement { | ||
const cacheId = `${address}:${diameter}`; | ||
const identicon: SVGSVGElement = | ||
this.cache[cacheId] || | ||
(this.cache[cacheId] = this.generateNewIdenticon(address, diameter)); | ||
const cleanCopy: SVGSVGElement = identicon.cloneNode(true) as SVGSVGElement; | ||
return cleanCopy; | ||
} | ||
|
||
/** | ||
* Generates a new Jazzicon SVG for a given address and diameter. | ||
* | ||
* @param address - The cryptocurrency address for the identicon. | ||
* @param diameter - The diameter of the identicon. | ||
* @returns A new Jazzicon SVG element. | ||
*/ | ||
generateNewIdenticon(address: string, diameter: number): SVGSVGElement { | ||
const numericRepresentation = jsNumberForAddress(address); | ||
const identicon = this.jazzicon(diameter, numericRepresentation); | ||
return identicon; | ||
} | ||
} | ||
|
||
let iconFactory: IconFactory | undefined; | ||
|
||
/** | ||
* Generates or retrieves an existing IconFactory instance. | ||
* | ||
* @param jazzicon - A function that returns a Jazzicon SVG given a diameter and seed. | ||
* @returns An IconFactory instance. | ||
*/ | ||
export default function iconFactoryGenerator( | ||
jazzicon: (diameter: number, seed: number) => SVGSVGElement, | ||
): IconFactory { | ||
if (!iconFactory) { | ||
iconFactory = new IconFactory(jazzicon); | ||
} | ||
return iconFactory; | ||
} | ||
|
||
/** | ||
* Determines if an icon already exists for a given address based on token metadata. | ||
* | ||
* @param address - The cryptocurrency address. | ||
* @param tokenMetadata - Metadata containing optional icon URL. | ||
* @returns True if an icon exists, otherwise false. | ||
*/ | ||
function iconExistsFor( | ||
address: string, | ||
tokenMetadata?: Partial<TokenMetadata>, | ||
): tokenMetadata is TokenMetadata { | ||
if (!tokenMetadata?.iconUrl) { | ||
return false; | ||
} | ||
return isValidHexAddress(address, { allowNonPrefixed: false }); | ||
} | ||
|
||
/** | ||
* Creates an HTMLImageElement for a given token metadata. | ||
* | ||
* @param tokenMetadata - Metadata containing the icon URL. Defaults to an empty object. | ||
* @returns An HTMLImageElement with the source set to the icon URL. | ||
*/ | ||
function imageElFor(tokenMetadata: TokenMetadata): HTMLImageElement { | ||
const img = document.createElement('img'); | ||
img.src = tokenMetadata.iconUrl; | ||
img.style.width = '100%'; | ||
return img; | ||
} | ||
|
||
/** | ||
* Converts a hexadecimal address into a numerical seed. | ||
* | ||
* @param address - The cryptocurrency address. | ||
* @returns A numerical seed derived from the address. | ||
*/ | ||
function jsNumberForAddress(address: string): number { | ||
const addr = address.slice(2, 10); | ||
const seed = parseInt(addr, 16); | ||
return seed; | ||
} |
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.
Should be boolean here?
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.
No, the
iconExistsFor
function acts as a type guard via a type predicate (thetokenMetadata is TokenMetadata
part).It enables the following, where
tokenMetadata
is optional iniconExistsFor
, but not inimageElFor
. TypeScript isn't "smart" enough to infer thattokenMetadata
is nevernull
/undefined
wheniconExistsFor
returnstrue
, so a type predicate is used to give TypeScript that information, otherwise we'd have to castTokenMetadata
or check fornull
/undefined
again inside theif
statement.See https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates for details.
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.
thanks ! it makes sense !