forked from Kieran/zap.stream
feat: emoji reactions
This commit is contained in:
parent
12ae084a9f
commit
2cdf88339a
@ -3,6 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emoji-mart/data": "^1.1.2",
|
||||||
|
"@emoji-mart/react": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.0.4",
|
"@radix-ui/react-dialog": "^1.0.4",
|
||||||
"@radix-ui/react-tabs": "^1.0.4",
|
"@radix-ui/react-tabs": "^1.0.4",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
@ -13,6 +15,7 @@
|
|||||||
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
|
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
|
||||||
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
|
"emoji-mart": "^5.5.2",
|
||||||
"hls.js": "^1.4.6",
|
"hls.js": "^1.4.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
@ -21,6 +24,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-intersection-observer": "^9.5.1",
|
"react-intersection-observer": "^9.5.1",
|
||||||
"react-router-dom": "^6.13.0",
|
"react-router-dom": "^6.13.0",
|
||||||
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"semantic-sdp": "^3.26.2",
|
"semantic-sdp": "^3.26.2",
|
||||||
"usehooks-ts": "^2.9.1",
|
"usehooks-ts": "^2.9.1",
|
||||||
"web-vitals": "^2.1.0",
|
"web-vitals": "^2.1.0",
|
||||||
|
@ -192,7 +192,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
background: black;
|
background: #0A0A0A;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
}
|
}
|
||||||
@ -236,6 +236,50 @@
|
|||||||
color: #FF8D2B;
|
color: #FF8D2B;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-zap-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #303030;
|
||||||
|
background: #111;
|
||||||
|
box-shadow: 0px 7px 4px 0px rgba(0, 0, 0, 0.25);
|
||||||
|
margin-top: 4px;
|
||||||
|
width: fit-content;
|
||||||
|
z-index: 1;
|
||||||
|
transition: opacity .3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1020px) {
|
||||||
|
.message-zap-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-zap-button {
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0px 4px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #FFFFFF66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-zap-button:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-zap-button-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.message-reactions {
|
.message-reactions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
@ -268,26 +312,3 @@
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-zap-button {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
left: 12px;
|
|
||||||
top: -6px;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
background: #434343;
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-zap-button-icon {
|
|
||||||
color: #FF8D2B;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
@ -16,8 +16,10 @@ import {
|
|||||||
type ChangeEvent,
|
type ChangeEvent,
|
||||||
type LegacyRef,
|
type LegacyRef,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useHover } from "usehooks-ts";
|
import { useHover, useOnClickOutside, useMediaQuery } from "usehooks-ts";
|
||||||
|
|
||||||
|
import data from "@emoji-mart/data";
|
||||||
|
import Picker from "@emoji-mart/react";
|
||||||
import useEmoji from "hooks/emoji";
|
import useEmoji from "hooks/emoji";
|
||||||
import { System } from "index";
|
import { System } from "index";
|
||||||
import { useLiveChatFeed } from "hooks/live-chat";
|
import { useLiveChatFeed } from "hooks/live-chat";
|
||||||
@ -141,6 +143,10 @@ function emojifyReaction(reaction: string) {
|
|||||||
return reaction;
|
return reaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Emoji {
|
||||||
|
native: string;
|
||||||
|
}
|
||||||
|
|
||||||
function ChatMessage({
|
function ChatMessage({
|
||||||
streamer,
|
streamer,
|
||||||
ev,
|
ev,
|
||||||
@ -153,7 +159,10 @@ function ChatMessage({
|
|||||||
reactions: readonly TaggedRawEvent[];
|
reactions: readonly TaggedRawEvent[];
|
||||||
}) {
|
}) {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isHovering = useHover(ref);
|
const emojiRef = useRef(null);
|
||||||
|
const isTablet = useMediaQuery("(max-width: 1020px)");
|
||||||
|
const [showZapDialog, setShowZapDialog] = useState(false);
|
||||||
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||||
const profile = useUserProfile(System, ev.pubkey);
|
const profile = useUserProfile(System, ev.pubkey);
|
||||||
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
||||||
const zaps = reactions
|
const zaps = reactions
|
||||||
@ -166,45 +175,53 @@ function ChatMessage({
|
|||||||
.map((ev) => emojifyReaction(ev.content));
|
.map((ev) => emojifyReaction(ev.content));
|
||||||
return [...new Set(emojified)];
|
return [...new Set(emojified)];
|
||||||
}, [ev, reactions]);
|
}, [ev, reactions]);
|
||||||
|
|
||||||
const hasReactions = emojis.length > 0;
|
const hasReactions = emojis.length > 0;
|
||||||
const totalZaps = useMemo(() => {
|
const totalZaps = useMemo(() => {
|
||||||
const messageZaps = zaps.filter((z) => z.event === ev.id);
|
const messageZaps = zaps.filter((z) => z.event === ev.id);
|
||||||
return messageZaps.reduce((acc, z) => acc + z.amount, 0);
|
return messageZaps.reduce((acc, z) => acc + z.amount, 0);
|
||||||
}, [reactions, ev]);
|
}, [reactions, ev]);
|
||||||
const hasZaps = totalZaps > 0;
|
const hasZaps = totalZaps > 0;
|
||||||
|
|
||||||
|
useOnClickOutside(ref, () => {
|
||||||
|
setShowZapDialog(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
useOnClickOutside(emojiRef, () => {
|
||||||
|
setShowEmojiPicker(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onEmojiSelect(emoji: Emoji) {
|
||||||
|
setShowEmojiPicker(false);
|
||||||
|
setShowZapDialog(false);
|
||||||
|
try {
|
||||||
|
const pub = await EventPublisher.nip7();
|
||||||
|
const reply = await pub?.generic((eb) => {
|
||||||
|
eb.kind(EventKind.Reaction)
|
||||||
|
.content(emoji.native)
|
||||||
|
.tag(["e", ev.id])
|
||||||
|
.tag(["p", ev.pubkey]);
|
||||||
|
return eb;
|
||||||
|
});
|
||||||
|
if (reply) {
|
||||||
|
console.debug(reply);
|
||||||
|
System.BroadcastEvent(reply);
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
const topOffset = ref.current?.getBoundingClientRect().top;
|
||||||
|
// @ts-expect-error
|
||||||
|
const leftOffset = ref.current?.getBoundingClientRect().left;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`message${link.author === ev.pubkey ? " streamer" : ""}`}
|
className={`message${link.author === ev.pubkey ? " streamer" : ""}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
onClick={() => setShowZapDialog(true)}
|
||||||
>
|
>
|
||||||
{zapTarget && (
|
|
||||||
<SendZapsDialog
|
|
||||||
lnurl={zapTarget}
|
|
||||||
aTag={
|
|
||||||
streamer === ev.pubkey
|
|
||||||
? `${link.kind}:${link.author}:${link.id}`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
eTag={ev.id}
|
|
||||||
pubkey={ev.pubkey}
|
|
||||||
button={
|
|
||||||
isHovering ? (
|
|
||||||
<div className="message-zap-container">
|
|
||||||
<button className="message-zap-button">
|
|
||||||
<Icon
|
|
||||||
name="zap-filled"
|
|
||||||
className="message-zap-button-icon"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
targetName={profile?.name || ev.pubkey}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Profile pubkey={ev.pubkey} />
|
<Profile pubkey={ev.pubkey} />
|
||||||
<Text content={ev.content} tags={ev.tags} />
|
<Text content={ev.content} tags={ev.tags} />
|
||||||
{(hasReactions || hasZaps) && (
|
{(hasReactions || hasZaps) && (
|
||||||
@ -222,7 +239,74 @@ function ChatMessage({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{ref.current && (
|
||||||
|
<div
|
||||||
|
className="message-zap-container"
|
||||||
|
style={
|
||||||
|
isTablet
|
||||||
|
? {
|
||||||
|
display: showZapDialog ? "flex" : "none",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
position: "fixed",
|
||||||
|
top: topOffset - 12,
|
||||||
|
left: leftOffset - 32,
|
||||||
|
opacity: showZapDialog ? 1 : 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{zapTarget && (
|
||||||
|
<SendZapsDialog
|
||||||
|
lnurl={zapTarget}
|
||||||
|
aTag={
|
||||||
|
streamer === ev.pubkey
|
||||||
|
? `${link.kind}:${link.author}:${link.id}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
eTag={ev.id}
|
||||||
|
pubkey={ev.pubkey}
|
||||||
|
button={
|
||||||
|
<button className="message-zap-button">
|
||||||
|
<Icon name="zap" className="message-zap-button-icon" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
targetName={profile?.name || ev.pubkey}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="message-zap-button"
|
||||||
|
onClick={() => setShowEmojiPicker(true)}
|
||||||
|
>
|
||||||
|
<Icon name="face" className="message-zap-button-icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{showEmojiPicker && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: topOffset - 310,
|
||||||
|
left: leftOffset,
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
ref={emojiRef}
|
||||||
|
>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
em-emoji-picker { max-height: 300px; }
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
<Picker
|
||||||
|
data={data}
|
||||||
|
perLine={7}
|
||||||
|
previewPosition="none"
|
||||||
|
skinTonePosition="search"
|
||||||
|
theme="dark"
|
||||||
|
onEmojiSelect={onEmojiSelect}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -21,5 +21,8 @@
|
|||||||
<symbol id="signal" viewBox="0 0 22 18" fill="none">
|
<symbol id="signal" viewBox="0 0 22 18" fill="none">
|
||||||
<path d="M15.2426 4.75735C17.5858 7.1005 17.5858 10.8995 15.2426 13.2426M6.75736 13.2426C4.41421 10.8995 4.41421 7.10046 6.75736 4.75732M3.92893 16.0711C0.0236893 12.1658 0.0236893 5.83417 3.92893 1.92892M18.0711 1.92897C21.9763 5.83421 21.9763 12.1659 18.0711 16.0711M13 8.99999C13 10.1046 12.1046 11 11 11C9.89543 11 9 10.1046 9 8.99999C9 7.89542 9.89543 6.99999 11 6.99999C12.1046 6.99999 13 7.89542 13 8.99999Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M15.2426 4.75735C17.5858 7.1005 17.5858 10.8995 15.2426 13.2426M6.75736 13.2426C4.41421 10.8995 4.41421 7.10046 6.75736 4.75732M3.92893 16.0711C0.0236893 12.1658 0.0236893 5.83417 3.92893 1.92892M18.0711 1.92897C21.9763 5.83421 21.9763 12.1659 18.0711 16.0711M13 8.99999C13 10.1046 12.1046 11 11 11C9.89543 11 9 10.1046 9 8.99999C9 7.89542 9.89543 6.99999 11 6.99999C12.1046 6.99999 13 7.89542 13 8.99999Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol id="face" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M15 9H15.01M9 9H9.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12ZM15.5 9C15.5 9.27614 15.2761 9.5 15 9.5C14.7239 9.5 14.5 9.27614 14.5 9C14.5 8.72386 14.7239 8.5 15 8.5C15.2761 8.5 15.5 8.72386 15.5 9ZM9.5 9C9.5 9.27614 9.27614 9.5 9 9.5C8.72386 9.5 8.5 9.27614 8.5 9C8.5 8.72386 8.72386 8.5 9 8.5C9.27614 8.5 9.5 8.72386 9.5 9ZM12 17.5C14.5005 17.5 16.5 15.667 16.5 14H7.5C7.5 15.667 9.4995 17.5 12 17.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</symbol>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.1 KiB |
20
yarn.lock
20
yarn.lock
@ -1028,6 +1028,16 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||||
|
|
||||||
|
"@emoji-mart/data@^1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513"
|
||||||
|
integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==
|
||||||
|
|
||||||
|
"@emoji-mart/react@^1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a"
|
||||||
|
integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0":
|
"@eslint-community/eslint-utils@^4.2.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||||
@ -3127,6 +3137,11 @@ electron-to-chromium@^1.4.431:
|
|||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.433.tgz#305ef5f8ea5fe65d252aae4b0e1088f9e4842533"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.433.tgz#305ef5f8ea5fe65d252aae4b0e1088f9e4842533"
|
||||||
integrity sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==
|
integrity sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==
|
||||||
|
|
||||||
|
emoji-mart@^5.5.2:
|
||||||
|
version "5.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.5.2.tgz#3ddbaf053139cf4aa217650078bc1c50ca8381af"
|
||||||
|
integrity sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==
|
||||||
|
|
||||||
emoji-regex@10.2.1, emoji-regex@^10.2.1:
|
emoji-regex@10.2.1, emoji-regex@^10.2.1:
|
||||||
version "10.2.1"
|
version "10.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f"
|
||||||
@ -5545,6 +5560,11 @@ requires-port@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||||
|
|
||||||
|
resize-observer-polyfill@^1.5.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||||
|
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||||
|
|
||||||
resolve-cwd@^3.0.0:
|
resolve-cwd@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||||
|
Loading…
Reference in New Issue
Block a user