From d8594f5f33db474bbdea94aacea58b6e77e36ffa Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Wed, 19 Jul 2023 14:32:06 +0530 Subject: [PATCH 001/214] chore: extract emojis from paste text --- src/CONST.js | 1 + src/libs/EmojiUtils.js | 44 ++++++++++++++++++- src/pages/home/report/ReportActionCompose.js | 2 +- .../report/ReportActionItemMessageEdit.js | 2 +- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 9a45cd631a90..85560caf0fd3 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1148,6 +1148,7 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, + CODE_EMOJIS: /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/gi, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 15f1d5301836..eed3865e3a4b 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -258,6 +258,47 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { return code; }; +/** + * Extracts emojis from a given text. + * + * @param {string} text - The text to extract emojis from. + * @returns {string[]} An array of emoji codes. + */ +function extractEmojis(text) { + const emojis = []; + if (!text || typeof text !== 'string') return emojis; + + let parseEmojis = text.match(CONST.REGEX.CODE_EMOJIS); + if (!parseEmojis) return emojis; + + parseEmojis = [...new Set(parseEmojis)]; + + for (let i = 0; i < parseEmojis.length; i++) { + const character = parseEmojis[i]; + const emoji = Emojis.emojiCodeTable[character]; + if (!emoji) continue; + emojis.push(emoji); + } + + return emojis; +} + +/** + * Find all emojis in a text and replace them with their code. + * @param {string} text + * @param {number} preferredSkinTone + * @param {string} lang + * @returns + */ +function replaceAndExtractEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { + const {text: newDraft = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); + + return { + text: newDraft, + emojis: emojis.concat(extractEmojis(text)), + }; +} + /** * Replace any emoji name in a text with the emoji icon. * If we're on mobile, we also add a space after the emoji granted there's no text after it. @@ -265,7 +306,7 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @param {String} text * @param {Number} preferredSkinTone * @param {String} lang - * @returns {Object} + * @returns {{text: String, emojis: Array}} */ function replaceEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { const trie = emojisTrie[lang]; @@ -416,4 +457,5 @@ export { getPreferredSkinToneIndex, getPreferredEmojiCode, getUniqueEmojiCodes, + replaceAndExtractEmojis, }; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 8155f09b2aac..5794349d809e 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -789,7 +789,7 @@ class ReportActionCompose extends React.Component { * @param {Boolean} shouldDebounceSaveComment */ updateComment(comment, shouldDebounceSaveComment) { - const {text: newComment = '', emojis = []} = EmojiUtils.replaceEmojis(comment, this.props.preferredSkinTone, this.props.preferredLocale); + const {text: newComment = '', emojis = []} = EmojiUtils.replaceAndExtractEmojis(comment, this.props.preferredSkinTone, this.props.preferredLocale); if (!_.isEmpty(emojis)) { this.insertedEmojis = [...this.insertedEmojis, ...emojis]; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index c7b39734961f..62f7c0fc1880 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -162,7 +162,7 @@ function ReportActionItemMessageEdit(props) { */ const updateDraft = useCallback( (newDraftInput) => { - const {text: newDraft = '', emojis = []} = EmojiUtils.replaceEmojis(newDraftInput, props.preferredSkinTone, props.preferredLocale); + const {text: newDraft = '', emojis = []} = EmojiUtils.replaceAndExtractEmojis(newDraftInput, props.preferredSkinTone, props.preferredLocale); if (!_.isEmpty(emojis)) { insertedEmojis.current = [...insertedEmojis.current, ...emojis]; From 29f0a45618c39333f2b184bcbff2b47c48e32b53 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Wed, 19 Jul 2023 14:52:14 +0530 Subject: [PATCH 002/214] chore: lint issue fix --- src/libs/EmojiUtils.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index eed3865e3a4b..69b0d6725433 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -276,29 +276,13 @@ function extractEmojis(text) { for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; const emoji = Emojis.emojiCodeTable[character]; - if (!emoji) continue; + if (!emoji) continue; // eslint-disable-line no-continue emojis.push(emoji); } return emojis; } -/** - * Find all emojis in a text and replace them with their code. - * @param {string} text - * @param {number} preferredSkinTone - * @param {string} lang - * @returns - */ -function replaceAndExtractEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { - const {text: newDraft = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); - - return { - text: newDraft, - emojis: emojis.concat(extractEmojis(text)), - }; -} - /** * Replace any emoji name in a text with the emoji icon. * If we're on mobile, we also add a space after the emoji granted there's no text after it. @@ -344,6 +328,22 @@ function replaceEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, return {text: newText, emojis}; } +/** + * Find all emojis in a text and replace them with their code. + * @param {string} text + * @param {number} preferredSkinTone + * @param {string} lang + * @returns {{text: string, emojis: string[]}} + */ +function replaceAndExtractEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { + const {text: newDraft = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); + + return { + text: newDraft, + emojis: emojis.concat(extractEmojis(text)), + }; +} + /** * Suggest emojis when typing emojis prefix after colon * @param {String} text From 61444f805e5002661ce7729d051acdf1365296e4 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 21 Jul 2023 02:46:29 +0530 Subject: [PATCH 003/214] chore: change jsdoc and varible names --- src/libs/EmojiUtils.js | 23 +++++++++++-------- src/pages/home/report/ReportActionCompose.js | 2 +- .../report/ReportActionItemMessageEdit.js | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 69b0d6725433..b1ad5b936d2e 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -261,15 +261,18 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { /** * Extracts emojis from a given text. * - * @param {string} text - The text to extract emojis from. - * @returns {string[]} An array of emoji codes. + * @param {String} text - The text to extract emojis from. + * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { const emojis = []; if (!text || typeof text !== 'string') return emojis; let parseEmojis = text.match(CONST.REGEX.CODE_EMOJIS); - if (!parseEmojis) return emojis; + + if (!parseEmojis) { + return emojis; + } parseEmojis = [...new Set(parseEmojis)]; @@ -290,7 +293,7 @@ function extractEmojis(text) { * @param {String} text * @param {Number} preferredSkinTone * @param {String} lang - * @returns {{text: String, emojis: Array}} + * @returns {Object} */ function replaceEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { const trie = emojisTrie[lang]; @@ -330,16 +333,16 @@ function replaceEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, /** * Find all emojis in a text and replace them with their code. - * @param {string} text - * @param {number} preferredSkinTone - * @param {string} lang - * @returns {{text: string, emojis: string[]}} + * @param {String} text + * @param {Number} preferredSkinTone + * @param {String} lang + * @returns {Object} */ function replaceAndExtractEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { - const {text: newDraft = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); + const {text: covertedText = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); return { - text: newDraft, + text: covertedText, emojis: emojis.concat(extractEmojis(text)), }; } diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 5794349d809e..e59acd640baf 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -789,7 +789,7 @@ class ReportActionCompose extends React.Component { * @param {Boolean} shouldDebounceSaveComment */ updateComment(comment, shouldDebounceSaveComment) { - const {text: newComment = '', emojis = []} = EmojiUtils.replaceAndExtractEmojis(comment, this.props.preferredSkinTone, this.props.preferredLocale); + const {text: newComment, emojis} = EmojiUtils.replaceAndExtractEmojis(comment, this.props.preferredSkinTone, this.props.preferredLocale); if (!_.isEmpty(emojis)) { this.insertedEmojis = [...this.insertedEmojis, ...emojis]; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 62f7c0fc1880..f4231ef50961 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -162,7 +162,7 @@ function ReportActionItemMessageEdit(props) { */ const updateDraft = useCallback( (newDraftInput) => { - const {text: newDraft = '', emojis = []} = EmojiUtils.replaceAndExtractEmojis(newDraftInput, props.preferredSkinTone, props.preferredLocale); + const {text: newDraft, emojis} = EmojiUtils.replaceAndExtractEmojis(newDraftInput, props.preferredSkinTone, props.preferredLocale); if (!_.isEmpty(emojis)) { insertedEmojis.current = [...insertedEmojis.current, ...emojis]; From 27f37b824531449f2ac99fc35c29455ea38db1e2 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 21 Jul 2023 02:47:37 +0530 Subject: [PATCH 004/214] chore: change condition for early return --- src/libs/EmojiUtils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index b1ad5b936d2e..eeb3774923a5 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -266,7 +266,9 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { */ function extractEmojis(text) { const emojis = []; - if (!text || typeof text !== 'string') return emojis; + if (!text) { + return emojis; + } let parseEmojis = text.match(CONST.REGEX.CODE_EMOJIS); From 2758401d1cd7aebc69f4775b50be2707ff6dd38d Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 21 Jul 2023 02:55:32 +0530 Subject: [PATCH 005/214] chore: remove extra regex and use the available one EMOJIS --- src/CONST.js | 1 - src/libs/EmojiUtils.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 483a87b4a3ce..b7a730821fdb 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1148,7 +1148,6 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, - CODE_EMOJIS: /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/gi, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index eeb3774923a5..56ffed9e1086 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -270,7 +270,7 @@ function extractEmojis(text) { return emojis; } - let parseEmojis = text.match(CONST.REGEX.CODE_EMOJIS); + let parseEmojis = text.match(CONST.REGEX.EMOJIS); if (!parseEmojis) { return emojis; From 8cccc8969d4356ce870c2a2bf7ac85e4bb8119b9 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Fri, 21 Jul 2023 12:37:04 +0530 Subject: [PATCH 006/214] chore: variable name spell fix --- src/libs/EmojiUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 56ffed9e1086..dee635089e00 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -341,10 +341,10 @@ function replaceEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, * @returns {Object} */ function replaceAndExtractEmojis(text, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, lang = CONST.LOCALES.DEFAULT) { - const {text: covertedText = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); + const {text: convertedText = '', emojis = []} = replaceEmojis(text, preferredSkinTone, lang); return { - text: covertedText, + text: convertedText, emojis: emojis.concat(extractEmojis(text)), }; } From e8b2632ad26fe50d1562e8727f5cbceffc1db17e Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Wed, 26 Jul 2023 02:22:01 +0530 Subject: [PATCH 007/214] fix: zwj sequences issue --- src/CONST.js | 2 ++ src/libs/EmojiUtils.js | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index b7a730821fdb..c4fd7a5174eb 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1148,6 +1148,8 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, + EMOJI_ZWJ_SEQUENCES: + /(๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘จโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ|๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€๐ŸŽ„|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€๐ŸŽ„|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€๐ŸŽ„|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€๐ŸŽ„|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐ŸŽ„|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿพ|๐Ÿ‘จโ€โš•๏ธ|๐Ÿ‘จโ€โš–๏ธ|๐Ÿ‘จโ€โœˆ๏ธ|๐Ÿ‘จโ€๐ŸŒพ|๐Ÿ‘จโ€๐Ÿณ|๐Ÿ‘จโ€๐Ÿผ|๐Ÿ‘จโ€๐ŸŽ“|๐Ÿ‘จโ€๐ŸŽค|๐Ÿ‘จโ€๐ŸŽจ|๐Ÿ‘จโ€๐Ÿซ|๐Ÿ‘จโ€๐Ÿญ|๐Ÿ‘จโ€๐Ÿ’ป|๐Ÿ‘จโ€๐Ÿ’ผ|๐Ÿ‘จโ€๐Ÿ”ง|๐Ÿ‘จโ€๐Ÿ”ฌ|๐Ÿ‘จโ€๐Ÿš€|๐Ÿ‘จโ€๐Ÿš’|๐Ÿ‘จโ€๐Ÿฆฏ|๐Ÿ‘จโ€๐Ÿฆผ|๐Ÿ‘จโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿปโ€โš•๏ธ|๐Ÿ‘จ๐Ÿปโ€โš–๏ธ|๐Ÿ‘จ๐Ÿปโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿปโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿปโ€๐Ÿณ|๐Ÿ‘จ๐Ÿปโ€๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿปโ€๐ŸŽค|๐Ÿ‘จ๐Ÿปโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿปโ€๐Ÿซ|๐Ÿ‘จ๐Ÿปโ€๐Ÿญ|๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿปโ€๐Ÿš€|๐Ÿ‘จ๐Ÿปโ€๐Ÿš’|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿผโ€โš•๏ธ|๐Ÿ‘จ๐Ÿผโ€โš–๏ธ|๐Ÿ‘จ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿผโ€๐Ÿณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿผโ€๐ŸŽค|๐Ÿ‘จ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿผโ€๐Ÿซ|๐Ÿ‘จ๐Ÿผโ€๐Ÿญ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿผโ€๐Ÿš€|๐Ÿ‘จ๐Ÿผโ€๐Ÿš’|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿฝโ€โš•๏ธ|๐Ÿ‘จ๐Ÿฝโ€โš–๏ธ|๐Ÿ‘จ๐Ÿฝโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿฝโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿณ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽค|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿซ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿญ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿš€|๐Ÿ‘จ๐Ÿฝโ€๐Ÿš’|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿพโ€โš•๏ธ|๐Ÿ‘จ๐Ÿพโ€โš–๏ธ|๐Ÿ‘จ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿพโ€๐Ÿณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿพโ€๐ŸŽค|๐Ÿ‘จ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿพโ€๐Ÿซ|๐Ÿ‘จ๐Ÿพโ€๐Ÿญ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿพโ€๐Ÿš€|๐Ÿ‘จ๐Ÿพโ€๐Ÿš’|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿฟโ€โš•๏ธ|๐Ÿ‘จ๐Ÿฟโ€โš–๏ธ|๐Ÿ‘จ๐Ÿฟโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿฟโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿณ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽค|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿซ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿญ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿš€|๐Ÿ‘จ๐Ÿฟโ€๐Ÿš’|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฝ|๐Ÿ‘ฉโ€โš•๏ธ|๐Ÿ‘ฉโ€โš–๏ธ|๐Ÿ‘ฉโ€โœˆ๏ธ|๐Ÿ‘ฉโ€๐ŸŒพ|๐Ÿ‘ฉโ€๐Ÿณ|๐Ÿ‘ฉโ€๐Ÿผ|๐Ÿ‘ฉโ€๐ŸŽ“|๐Ÿ‘ฉโ€๐ŸŽค|๐Ÿ‘ฉโ€๐ŸŽจ|๐Ÿ‘ฉโ€๐Ÿซ|๐Ÿ‘ฉโ€๐Ÿญ|๐Ÿ‘ฉโ€๐Ÿ’ป|๐Ÿ‘ฉโ€๐Ÿ’ผ|๐Ÿ‘ฉโ€๐Ÿ”ง|๐Ÿ‘ฉโ€๐Ÿ”ฌ|๐Ÿ‘ฉโ€๐Ÿš€|๐Ÿ‘ฉโ€๐Ÿš’|๐Ÿ‘ฉโ€๐Ÿฆฏ|๐Ÿ‘ฉโ€๐Ÿฆผ|๐Ÿ‘ฉโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿปโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿปโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿปโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿผโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿฝโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿพโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿฟโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฝ|๐Ÿง‘โ€โš•๏ธ|๐Ÿง‘โ€โš–๏ธ|๐Ÿง‘โ€โœˆ๏ธ|๐Ÿง‘โ€๐ŸŒพ|๐Ÿง‘โ€๐Ÿณ|๐Ÿง‘โ€๐Ÿผ|๐Ÿง‘โ€๐ŸŽ“|๐Ÿง‘โ€๐ŸŽค|๐Ÿง‘โ€๐ŸŽจ|๐Ÿง‘โ€๐Ÿซ|๐Ÿง‘โ€๐Ÿญ|๐Ÿง‘โ€๐Ÿ’ป|๐Ÿง‘โ€๐Ÿ’ผ|๐Ÿง‘โ€๐Ÿ”ง|๐Ÿง‘โ€๐Ÿ”ฌ|๐Ÿง‘โ€๐Ÿš€|๐Ÿง‘โ€๐Ÿš’|๐Ÿง‘โ€๐Ÿฆฏ|๐Ÿง‘โ€๐Ÿฆผ|๐Ÿง‘โ€๐Ÿฆฝ|๐Ÿง‘๐Ÿปโ€โš•๏ธ|๐Ÿง‘๐Ÿปโ€โš–๏ธ|๐Ÿง‘๐Ÿปโ€โœˆ๏ธ|๐Ÿง‘๐Ÿปโ€๐ŸŒพ|๐Ÿง‘๐Ÿปโ€๐Ÿณ|๐Ÿง‘๐Ÿปโ€๐Ÿผ|๐Ÿง‘๐Ÿปโ€๐ŸŽ“|๐Ÿง‘๐Ÿปโ€๐ŸŽค|๐Ÿง‘๐Ÿปโ€๐ŸŽจ|๐Ÿง‘๐Ÿปโ€๐Ÿซ|๐Ÿง‘๐Ÿปโ€๐Ÿญ|๐Ÿง‘๐Ÿปโ€๐Ÿ’ป|๐Ÿง‘๐Ÿปโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿปโ€๐Ÿ”ง|๐Ÿง‘๐Ÿปโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿปโ€๐Ÿš€|๐Ÿง‘๐Ÿปโ€๐Ÿš’|๐Ÿง‘๐Ÿปโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿปโ€๐Ÿฆผ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿผโ€โš•๏ธ|๐Ÿง‘๐Ÿผโ€โš–๏ธ|๐Ÿง‘๐Ÿผโ€โœˆ๏ธ|๐Ÿง‘๐Ÿผโ€๐ŸŒพ|๐Ÿง‘๐Ÿผโ€๐Ÿณ|๐Ÿง‘๐Ÿผโ€๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐ŸŽ“|๐Ÿง‘๐Ÿผโ€๐ŸŽค|๐Ÿง‘๐Ÿผโ€๐ŸŽจ|๐Ÿง‘๐Ÿผโ€๐Ÿซ|๐Ÿง‘๐Ÿผโ€๐Ÿญ|๐Ÿง‘๐Ÿผโ€๐Ÿ’ป|๐Ÿง‘๐Ÿผโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿผโ€๐Ÿ”ง|๐Ÿง‘๐Ÿผโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿผโ€๐Ÿš€|๐Ÿง‘๐Ÿผโ€๐Ÿš’|๐Ÿง‘๐Ÿผโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿผโ€๐Ÿฆผ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿฝโ€โš•๏ธ|๐Ÿง‘๐Ÿฝโ€โš–๏ธ|๐Ÿง‘๐Ÿฝโ€โœˆ๏ธ|๐Ÿง‘๐Ÿฝโ€๐ŸŒพ|๐Ÿง‘๐Ÿฝโ€๐Ÿณ|๐Ÿง‘๐Ÿฝโ€๐Ÿผ|๐Ÿง‘๐Ÿฝโ€๐ŸŽ“|๐Ÿง‘๐Ÿฝโ€๐ŸŽค|๐Ÿง‘๐Ÿฝโ€๐ŸŽจ|๐Ÿง‘๐Ÿฝโ€๐Ÿซ|๐Ÿง‘๐Ÿฝโ€๐Ÿญ|๐Ÿง‘๐Ÿฝโ€๐Ÿ’ป|๐Ÿง‘๐Ÿฝโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿฝโ€๐Ÿ”ง|๐Ÿง‘๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿฝโ€๐Ÿš€|๐Ÿง‘๐Ÿฝโ€๐Ÿš’|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆผ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿพโ€โš•๏ธ|๐Ÿง‘๐Ÿพโ€โš–๏ธ|๐Ÿง‘๐Ÿพโ€โœˆ๏ธ|๐Ÿง‘๐Ÿพโ€๐ŸŒพ|๐Ÿง‘๐Ÿพโ€๐Ÿณ|๐Ÿง‘๐Ÿพโ€๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐ŸŽ“|๐Ÿง‘๐Ÿพโ€๐ŸŽค|๐Ÿง‘๐Ÿพโ€๐ŸŽจ|๐Ÿง‘๐Ÿพโ€๐Ÿซ|๐Ÿง‘๐Ÿพโ€๐Ÿญ|๐Ÿง‘๐Ÿพโ€๐Ÿ’ป|๐Ÿง‘๐Ÿพโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿพโ€๐Ÿ”ง|๐Ÿง‘๐Ÿพโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿพโ€๐Ÿš€|๐Ÿง‘๐Ÿพโ€๐Ÿš’|๐Ÿง‘๐Ÿพโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿพโ€๐Ÿฆผ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿฟโ€โš•๏ธ|๐Ÿง‘๐Ÿฟโ€โš–๏ธ|๐Ÿง‘๐Ÿฟโ€โœˆ๏ธ|๐Ÿง‘๐Ÿฟโ€๐ŸŒพ|๐Ÿง‘๐Ÿฟโ€๐Ÿณ|๐Ÿง‘๐Ÿฟโ€๐Ÿผ|๐Ÿง‘๐Ÿฟโ€๐ŸŽ“|๐Ÿง‘๐Ÿฟโ€๐ŸŽค|๐Ÿง‘๐Ÿฟโ€๐ŸŽจ|๐Ÿง‘๐Ÿฟโ€๐Ÿซ|๐Ÿง‘๐Ÿฟโ€๐Ÿญ|๐Ÿง‘๐Ÿฟโ€๐Ÿ’ป|๐Ÿง‘๐Ÿฟโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿฟโ€๐Ÿ”ง|๐Ÿง‘๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿฟโ€๐Ÿš€|๐Ÿง‘๐Ÿฟโ€๐Ÿš’|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆผ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฝ|โ›น๐Ÿปโ€โ™€๏ธ|โ›น๐Ÿปโ€โ™‚๏ธ|โ›น๐Ÿผโ€โ™€๏ธ|โ›น๐Ÿผโ€โ™‚๏ธ|โ›น๐Ÿฝโ€โ™€๏ธ|โ›น๐Ÿฝโ€โ™‚๏ธ|โ›น๐Ÿพโ€โ™€๏ธ|โ›น๐Ÿพโ€โ™‚๏ธ|โ›น๐Ÿฟโ€โ™€๏ธ|โ›น๐Ÿฟโ€โ™‚๏ธ|โ›น๏ธโ€โ™€๏ธ|โ›น๏ธโ€โ™‚๏ธ|๐Ÿƒโ€โ™€๏ธ|๐Ÿƒโ€โ™‚๏ธ|๐Ÿƒ๐Ÿปโ€โ™€๏ธ|๐Ÿƒ๐Ÿปโ€โ™‚๏ธ|๐Ÿƒ๐Ÿผโ€โ™€๏ธ|๐Ÿƒ๐Ÿผโ€โ™‚๏ธ|๐Ÿƒ๐Ÿฝโ€โ™€๏ธ|๐Ÿƒ๐Ÿฝโ€โ™‚๏ธ|๐Ÿƒ๐Ÿพโ€โ™€๏ธ|๐Ÿƒ๐Ÿพโ€โ™‚๏ธ|๐Ÿƒ๐Ÿฟโ€โ™€๏ธ|๐Ÿƒ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ„โ€โ™€๏ธ|๐Ÿ„โ€โ™‚๏ธ|๐Ÿ„๐Ÿปโ€โ™€๏ธ|๐Ÿ„๐Ÿปโ€โ™‚๏ธ|๐Ÿ„๐Ÿผโ€โ™€๏ธ|๐Ÿ„๐Ÿผโ€โ™‚๏ธ|๐Ÿ„๐Ÿฝโ€โ™€๏ธ|๐Ÿ„๐Ÿฝโ€โ™‚๏ธ|๐Ÿ„๐Ÿพโ€โ™€๏ธ|๐Ÿ„๐Ÿพโ€โ™‚๏ธ|๐Ÿ„๐Ÿฟโ€โ™€๏ธ|๐Ÿ„๐Ÿฟโ€โ™‚๏ธ|๐ŸŠโ€โ™€๏ธ|๐ŸŠโ€โ™‚๏ธ|๐ŸŠ๐Ÿปโ€โ™€๏ธ|๐ŸŠ๐Ÿปโ€โ™‚๏ธ|๐ŸŠ๐Ÿผโ€โ™€๏ธ|๐ŸŠ๐Ÿผโ€โ™‚๏ธ|๐ŸŠ๐Ÿฝโ€โ™€๏ธ|๐ŸŠ๐Ÿฝโ€โ™‚๏ธ|๐ŸŠ๐Ÿพโ€โ™€๏ธ|๐ŸŠ๐Ÿพโ€โ™‚๏ธ|๐ŸŠ๐Ÿฟโ€โ™€๏ธ|๐ŸŠ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‹๐Ÿปโ€โ™€๏ธ|๐Ÿ‹๐Ÿปโ€โ™‚๏ธ|๐Ÿ‹๐Ÿผโ€โ™€๏ธ|๐Ÿ‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ‹๐Ÿฝโ€โ™€๏ธ|๐Ÿ‹๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‹๐Ÿพโ€โ™€๏ธ|๐Ÿ‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ‹๐Ÿฟโ€โ™€๏ธ|๐Ÿ‹๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‹๏ธโ€โ™€๏ธ|๐Ÿ‹๏ธโ€โ™‚๏ธ|๐ŸŒ๐Ÿปโ€โ™€๏ธ|๐ŸŒ๐Ÿปโ€โ™‚๏ธ|๐ŸŒ๐Ÿผโ€โ™€๏ธ|๐ŸŒ๐Ÿผโ€โ™‚๏ธ|๐ŸŒ๐Ÿฝโ€โ™€๏ธ|๐ŸŒ๐Ÿฝโ€โ™‚๏ธ|๐ŸŒ๐Ÿพโ€โ™€๏ธ|๐ŸŒ๐Ÿพโ€โ™‚๏ธ|๐ŸŒ๐Ÿฟโ€โ™€๏ธ|๐ŸŒ๐Ÿฟโ€โ™‚๏ธ|๐ŸŒ๏ธโ€โ™€๏ธ|๐ŸŒ๏ธโ€โ™‚๏ธ|๐Ÿ‘ฎโ€โ™€๏ธ|๐Ÿ‘ฎโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ฏโ€โ™€๏ธ|๐Ÿ‘ฏโ€โ™‚๏ธ|๐Ÿ‘ฐโ€โ™€๏ธ|๐Ÿ‘ฐโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ฑโ€โ™€๏ธ|๐Ÿ‘ฑโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ณโ€โ™€๏ธ|๐Ÿ‘ณโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ทโ€โ™€๏ธ|๐Ÿ‘ทโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’โ€โ™€๏ธ|๐Ÿ’โ€โ™‚๏ธ|๐Ÿ’๐Ÿปโ€โ™€๏ธ|๐Ÿ’๐Ÿปโ€โ™‚๏ธ|๐Ÿ’๐Ÿผโ€โ™€๏ธ|๐Ÿ’๐Ÿผโ€โ™‚๏ธ|๐Ÿ’๐Ÿฝโ€โ™€๏ธ|๐Ÿ’๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’๐Ÿพโ€โ™€๏ธ|๐Ÿ’๐Ÿพโ€โ™‚๏ธ|๐Ÿ’๐Ÿฟโ€โ™€๏ธ|๐Ÿ’๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’‚โ€โ™€๏ธ|๐Ÿ’‚โ€โ™‚๏ธ|๐Ÿ’‚๐Ÿปโ€โ™€๏ธ|๐Ÿ’‚๐Ÿปโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿผโ€โ™€๏ธ|๐Ÿ’‚๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿฝโ€โ™€๏ธ|๐Ÿ’‚๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿพโ€โ™€๏ธ|๐Ÿ’‚๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿฟโ€โ™€๏ธ|๐Ÿ’‚๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’†โ€โ™€๏ธ|๐Ÿ’†โ€โ™‚๏ธ|๐Ÿ’†๐Ÿปโ€โ™€๏ธ|๐Ÿ’†๐Ÿปโ€โ™‚๏ธ|๐Ÿ’†๐Ÿผโ€โ™€๏ธ|๐Ÿ’†๐Ÿผโ€โ™‚๏ธ|๐Ÿ’†๐Ÿฝโ€โ™€๏ธ|๐Ÿ’†๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’†๐Ÿพโ€โ™€๏ธ|๐Ÿ’†๐Ÿพโ€โ™‚๏ธ|๐Ÿ’†๐Ÿฟโ€โ™€๏ธ|๐Ÿ’†๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’‡โ€โ™€๏ธ|๐Ÿ’‡โ€โ™‚๏ธ|๐Ÿ’‡๐Ÿปโ€โ™€๏ธ|๐Ÿ’‡๐Ÿปโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿผโ€โ™€๏ธ|๐Ÿ’‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿฝโ€โ™€๏ธ|๐Ÿ’‡๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿพโ€โ™€๏ธ|๐Ÿ’‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿฟโ€โ™€๏ธ|๐Ÿ’‡๐Ÿฟโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿปโ€โ™€๏ธ|๐Ÿ•ต๐Ÿปโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿผโ€โ™€๏ธ|๐Ÿ•ต๐Ÿผโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿฝโ€โ™€๏ธ|๐Ÿ•ต๐Ÿฝโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿพโ€โ™€๏ธ|๐Ÿ•ต๐Ÿพโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿฟโ€โ™€๏ธ|๐Ÿ•ต๐Ÿฟโ€โ™‚๏ธ|๐Ÿ•ต๏ธโ€โ™€๏ธ|๐Ÿ•ต๏ธโ€โ™‚๏ธ|๐Ÿ™…โ€โ™€๏ธ|๐Ÿ™…โ€โ™‚๏ธ|๐Ÿ™…๐Ÿปโ€โ™€๏ธ|๐Ÿ™…๐Ÿปโ€โ™‚๏ธ|๐Ÿ™…๐Ÿผโ€โ™€๏ธ|๐Ÿ™…๐Ÿผโ€โ™‚๏ธ|๐Ÿ™…๐Ÿฝโ€โ™€๏ธ|๐Ÿ™…๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™…๐Ÿพโ€โ™€๏ธ|๐Ÿ™…๐Ÿพโ€โ™‚๏ธ|๐Ÿ™…๐Ÿฟโ€โ™€๏ธ|๐Ÿ™…๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™†โ€โ™€๏ธ|๐Ÿ™†โ€โ™‚๏ธ|๐Ÿ™†๐Ÿปโ€โ™€๏ธ|๐Ÿ™†๐Ÿปโ€โ™‚๏ธ|๐Ÿ™†๐Ÿผโ€โ™€๏ธ|๐Ÿ™†๐Ÿผโ€โ™‚๏ธ|๐Ÿ™†๐Ÿฝโ€โ™€๏ธ|๐Ÿ™†๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™†๐Ÿพโ€โ™€๏ธ|๐Ÿ™†๐Ÿพโ€โ™‚๏ธ|๐Ÿ™†๐Ÿฟโ€โ™€๏ธ|๐Ÿ™†๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™‡โ€โ™€๏ธ|๐Ÿ™‡โ€โ™‚๏ธ|๐Ÿ™‡๐Ÿปโ€โ™€๏ธ|๐Ÿ™‡๐Ÿปโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿผโ€โ™€๏ธ|๐Ÿ™‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿฝโ€โ™€๏ธ|๐Ÿ™‡๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿพโ€โ™€๏ธ|๐Ÿ™‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿฟโ€โ™€๏ธ|๐Ÿ™‡๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™‹โ€โ™€๏ธ|๐Ÿ™‹โ€โ™‚๏ธ|๐Ÿ™‹๐Ÿปโ€โ™€๏ธ|๐Ÿ™‹๐Ÿปโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿผโ€โ™€๏ธ|๐Ÿ™‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿฝโ€โ™€๏ธ|๐Ÿ™‹๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿพโ€โ™€๏ธ|๐Ÿ™‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿฟโ€โ™€๏ธ|๐Ÿ™‹๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™โ€โ™€๏ธ|๐Ÿ™โ€โ™‚๏ธ|๐Ÿ™๐Ÿปโ€โ™€๏ธ|๐Ÿ™๐Ÿปโ€โ™‚๏ธ|๐Ÿ™๐Ÿผโ€โ™€๏ธ|๐Ÿ™๐Ÿผโ€โ™‚๏ธ|๐Ÿ™๐Ÿฝโ€โ™€๏ธ|๐Ÿ™๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™๐Ÿพโ€โ™€๏ธ|๐Ÿ™๐Ÿพโ€โ™‚๏ธ|๐Ÿ™๐Ÿฟโ€โ™€๏ธ|๐Ÿ™๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™Žโ€โ™€๏ธ|๐Ÿ™Žโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿปโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿปโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿฝโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿฟโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿฟโ€โ™‚๏ธ|๐Ÿšฃโ€โ™€๏ธ|๐Ÿšฃโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿปโ€โ™€๏ธ|๐Ÿšฃ๐Ÿปโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿผโ€โ™€๏ธ|๐Ÿšฃ๐Ÿผโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿฝโ€โ™€๏ธ|๐Ÿšฃ๐Ÿฝโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿพโ€โ™€๏ธ|๐Ÿšฃ๐Ÿพโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿฟโ€โ™€๏ธ|๐Ÿšฃ๐Ÿฟโ€โ™‚๏ธ|๐Ÿšดโ€โ™€๏ธ|๐Ÿšดโ€โ™‚๏ธ|๐Ÿšด๐Ÿปโ€โ™€๏ธ|๐Ÿšด๐Ÿปโ€โ™‚๏ธ|๐Ÿšด๐Ÿผโ€โ™€๏ธ|๐Ÿšด๐Ÿผโ€โ™‚๏ธ|๐Ÿšด๐Ÿฝโ€โ™€๏ธ|๐Ÿšด๐Ÿฝโ€โ™‚๏ธ|๐Ÿšด๐Ÿพโ€โ™€๏ธ|๐Ÿšด๐Ÿพโ€โ™‚๏ธ|๐Ÿšด๐Ÿฟโ€โ™€๏ธ|๐Ÿšด๐Ÿฟโ€โ™‚๏ธ|๐Ÿšตโ€โ™€๏ธ|๐Ÿšตโ€โ™‚๏ธ|๐Ÿšต๐Ÿปโ€โ™€๏ธ|๐Ÿšต๐Ÿปโ€โ™‚๏ธ|๐Ÿšต๐Ÿผโ€โ™€๏ธ|๐Ÿšต๐Ÿผโ€โ™‚๏ธ|๐Ÿšต๐Ÿฝโ€โ™€๏ธ|๐Ÿšต๐Ÿฝโ€โ™‚๏ธ|๐Ÿšต๐Ÿพโ€โ™€๏ธ|๐Ÿšต๐Ÿพโ€โ™‚๏ธ|๐Ÿšต๐Ÿฟโ€โ™€๏ธ|๐Ÿšต๐Ÿฟโ€โ™‚๏ธ|๐Ÿšถโ€โ™€๏ธ|๐Ÿšถโ€โ™‚๏ธ|๐Ÿšถ๐Ÿปโ€โ™€๏ธ|๐Ÿšถ๐Ÿปโ€โ™‚๏ธ|๐Ÿšถ๐Ÿผโ€โ™€๏ธ|๐Ÿšถ๐Ÿผโ€โ™‚๏ธ|๐Ÿšถ๐Ÿฝโ€โ™€๏ธ|๐Ÿšถ๐Ÿฝโ€โ™‚๏ธ|๐Ÿšถ๐Ÿพโ€โ™€๏ธ|๐Ÿšถ๐Ÿพโ€โ™‚๏ธ|๐Ÿšถ๐Ÿฟโ€โ™€๏ธ|๐Ÿšถ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคฆโ€โ™€๏ธ|๐Ÿคฆโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿปโ€โ™€๏ธ|๐Ÿคฆ๐Ÿปโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿผโ€โ™€๏ธ|๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿฝโ€โ™€๏ธ|๐Ÿคฆ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿพโ€โ™€๏ธ|๐Ÿคฆ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿฟโ€โ™€๏ธ|๐Ÿคฆ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคตโ€โ™€๏ธ|๐Ÿคตโ€โ™‚๏ธ|๐Ÿคต๐Ÿปโ€โ™€๏ธ|๐Ÿคต๐Ÿปโ€โ™‚๏ธ|๐Ÿคต๐Ÿผโ€โ™€๏ธ|๐Ÿคต๐Ÿผโ€โ™‚๏ธ|๐Ÿคต๐Ÿฝโ€โ™€๏ธ|๐Ÿคต๐Ÿฝโ€โ™‚๏ธ|๐Ÿคต๐Ÿพโ€โ™€๏ธ|๐Ÿคต๐Ÿพโ€โ™‚๏ธ|๐Ÿคต๐Ÿฟโ€โ™€๏ธ|๐Ÿคต๐Ÿฟโ€โ™‚๏ธ|๐Ÿคทโ€โ™€๏ธ|๐Ÿคทโ€โ™‚๏ธ|๐Ÿคท๐Ÿปโ€โ™€๏ธ|๐Ÿคท๐Ÿปโ€โ™‚๏ธ|๐Ÿคท๐Ÿผโ€โ™€๏ธ|๐Ÿคท๐Ÿผโ€โ™‚๏ธ|๐Ÿคท๐Ÿฝโ€โ™€๏ธ|๐Ÿคท๐Ÿฝโ€โ™‚๏ธ|๐Ÿคท๐Ÿพโ€โ™€๏ธ|๐Ÿคท๐Ÿพโ€โ™‚๏ธ|๐Ÿคท๐Ÿฟโ€โ™€๏ธ|๐Ÿคท๐Ÿฟโ€โ™‚๏ธ|๐Ÿคธโ€โ™€๏ธ|๐Ÿคธโ€โ™‚๏ธ|๐Ÿคธ๐Ÿปโ€โ™€๏ธ|๐Ÿคธ๐Ÿปโ€โ™‚๏ธ|๐Ÿคธ๐Ÿผโ€โ™€๏ธ|๐Ÿคธ๐Ÿผโ€โ™‚๏ธ|๐Ÿคธ๐Ÿฝโ€โ™€๏ธ|๐Ÿคธ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคธ๐Ÿพโ€โ™€๏ธ|๐Ÿคธ๐Ÿพโ€โ™‚๏ธ|๐Ÿคธ๐Ÿฟโ€โ™€๏ธ|๐Ÿคธ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคนโ€โ™€๏ธ|๐Ÿคนโ€โ™‚๏ธ|๐Ÿคน๐Ÿปโ€โ™€๏ธ|๐Ÿคน๐Ÿปโ€โ™‚๏ธ|๐Ÿคน๐Ÿผโ€โ™€๏ธ|๐Ÿคน๐Ÿผโ€โ™‚๏ธ|๐Ÿคน๐Ÿฝโ€โ™€๏ธ|๐Ÿคน๐Ÿฝโ€โ™‚๏ธ|๐Ÿคน๐Ÿพโ€โ™€๏ธ|๐Ÿคน๐Ÿพโ€โ™‚๏ธ|๐Ÿคน๐Ÿฟโ€โ™€๏ธ|๐Ÿคน๐Ÿฟโ€โ™‚๏ธ|๐Ÿคผโ€โ™€๏ธ|๐Ÿคผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™€๏ธ|๐Ÿคฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿปโ€โ™€๏ธ|๐Ÿคฝ๐Ÿปโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿผโ€โ™€๏ธ|๐Ÿคฝ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿฝโ€โ™€๏ธ|๐Ÿคฝ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿพโ€โ™€๏ธ|๐Ÿคฝ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿฟโ€โ™€๏ธ|๐Ÿคฝ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคพโ€โ™€๏ธ|๐Ÿคพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿปโ€โ™€๏ธ|๐Ÿคพ๐Ÿปโ€โ™‚๏ธ|๐Ÿคพ๐Ÿผโ€โ™€๏ธ|๐Ÿคพ๐Ÿผโ€โ™‚๏ธ|๐Ÿคพ๐Ÿฝโ€โ™€๏ธ|๐Ÿคพ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคพ๐Ÿพโ€โ™€๏ธ|๐Ÿคพ๐Ÿพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿฟโ€โ™€๏ธ|๐Ÿคพ๐Ÿฟโ€โ™‚๏ธ|๐Ÿฆธโ€โ™€๏ธ|๐Ÿฆธโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿปโ€โ™€๏ธ|๐Ÿฆธ๐Ÿปโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿผโ€โ™€๏ธ|๐Ÿฆธ๐Ÿผโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿฝโ€โ™€๏ธ|๐Ÿฆธ๐Ÿฝโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿพโ€โ™€๏ธ|๐Ÿฆธ๐Ÿพโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿฟโ€โ™€๏ธ|๐Ÿฆธ๐Ÿฟโ€โ™‚๏ธ|๐Ÿฆนโ€โ™€๏ธ|๐Ÿฆนโ€โ™‚๏ธ|๐Ÿฆน๐Ÿปโ€โ™€๏ธ|๐Ÿฆน๐Ÿปโ€โ™‚๏ธ|๐Ÿฆน๐Ÿผโ€โ™€๏ธ|๐Ÿฆน๐Ÿผโ€โ™‚๏ธ|๐Ÿฆน๐Ÿฝโ€โ™€๏ธ|๐Ÿฆน๐Ÿฝโ€โ™‚๏ธ|๐Ÿฆน๐Ÿพโ€โ™€๏ธ|๐Ÿฆน๐Ÿพโ€โ™‚๏ธ|๐Ÿฆน๐Ÿฟโ€โ™€๏ธ|๐Ÿฆน๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐ŸงŽโ€โ™€๏ธ|๐ŸงŽโ€โ™‚๏ธ|๐ŸงŽ๐Ÿปโ€โ™€๏ธ|๐ŸงŽ๐Ÿปโ€โ™‚๏ธ|๐ŸงŽ๐Ÿผโ€โ™€๏ธ|๐ŸงŽ๐Ÿผโ€โ™‚๏ธ|๐ŸงŽ๐Ÿฝโ€โ™€๏ธ|๐ŸงŽ๐Ÿฝโ€โ™‚๏ธ|๐ŸงŽ๐Ÿพโ€โ™€๏ธ|๐ŸงŽ๐Ÿพโ€โ™‚๏ธ|๐ŸงŽ๐Ÿฟโ€โ™€๏ธ|๐ŸงŽ๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐Ÿง”โ€โ™€๏ธ|๐Ÿง”โ€โ™‚๏ธ|๐Ÿง”๐Ÿปโ€โ™€๏ธ|๐Ÿง”๐Ÿปโ€โ™‚๏ธ|๐Ÿง”๐Ÿผโ€โ™€๏ธ|๐Ÿง”๐Ÿผโ€โ™‚๏ธ|๐Ÿง”๐Ÿฝโ€โ™€๏ธ|๐Ÿง”๐Ÿฝโ€โ™‚๏ธ|๐Ÿง”๐Ÿพโ€โ™€๏ธ|๐Ÿง”๐Ÿพโ€โ™‚๏ธ|๐Ÿง”๐Ÿฟโ€โ™€๏ธ|๐Ÿง”๐Ÿฟโ€โ™‚๏ธ|๐Ÿง–โ€โ™€๏ธ|๐Ÿง–โ€โ™‚๏ธ|๐Ÿง–๐Ÿปโ€โ™€๏ธ|๐Ÿง–๐Ÿปโ€โ™‚๏ธ|๐Ÿง–๐Ÿผโ€โ™€๏ธ|๐Ÿง–๐Ÿผโ€โ™‚๏ธ|๐Ÿง–๐Ÿฝโ€โ™€๏ธ|๐Ÿง–๐Ÿฝโ€โ™‚๏ธ|๐Ÿง–๐Ÿพโ€โ™€๏ธ|๐Ÿง–๐Ÿพโ€โ™‚๏ธ|๐Ÿง–๐Ÿฟโ€โ™€๏ธ|๐Ÿง–๐Ÿฟโ€โ™‚๏ธ|๐Ÿง—โ€โ™€๏ธ|๐Ÿง—โ€โ™‚๏ธ|๐Ÿง—๐Ÿปโ€โ™€๏ธ|๐Ÿง—๐Ÿปโ€โ™‚๏ธ|๐Ÿง—๐Ÿผโ€โ™€๏ธ|๐Ÿง—๐Ÿผโ€โ™‚๏ธ|๐Ÿง—๐Ÿฝโ€โ™€๏ธ|๐Ÿง—๐Ÿฝโ€โ™‚๏ธ|๐Ÿง—๐Ÿพโ€โ™€๏ธ|๐Ÿง—๐Ÿพโ€โ™‚๏ธ|๐Ÿง—๐Ÿฟโ€โ™€๏ธ|๐Ÿง—๐Ÿฟโ€โ™‚๏ธ|๐Ÿง˜โ€โ™€๏ธ|๐Ÿง˜โ€โ™‚๏ธ|๐Ÿง˜๐Ÿปโ€โ™€๏ธ|๐Ÿง˜๐Ÿปโ€โ™‚๏ธ|๐Ÿง˜๐Ÿผโ€โ™€๏ธ|๐Ÿง˜๐Ÿผโ€โ™‚๏ธ|๐Ÿง˜๐Ÿฝโ€โ™€๏ธ|๐Ÿง˜๐Ÿฝโ€โ™‚๏ธ|๐Ÿง˜๐Ÿพโ€โ™€๏ธ|๐Ÿง˜๐Ÿพโ€โ™‚๏ธ|๐Ÿง˜๐Ÿฟโ€โ™€๏ธ|๐Ÿง˜๐Ÿฟโ€โ™‚๏ธ|๐Ÿง™โ€โ™€๏ธ|๐Ÿง™โ€โ™‚๏ธ|๐Ÿง™๐Ÿปโ€โ™€๏ธ|๐Ÿง™๐Ÿปโ€โ™‚๏ธ|๐Ÿง™๐Ÿผโ€โ™€๏ธ|๐Ÿง™๐Ÿผโ€โ™‚๏ธ|๐Ÿง™๐Ÿฝโ€โ™€๏ธ|๐Ÿง™๐Ÿฝโ€โ™‚๏ธ|๐Ÿง™๐Ÿพโ€โ™€๏ธ|๐Ÿง™๐Ÿพโ€โ™‚๏ธ|๐Ÿง™๐Ÿฟโ€โ™€๏ธ|๐Ÿง™๐Ÿฟโ€โ™‚๏ธ|๐Ÿงšโ€โ™€๏ธ|๐Ÿงšโ€โ™‚๏ธ|๐Ÿงš๐Ÿปโ€โ™€๏ธ|๐Ÿงš๐Ÿปโ€โ™‚๏ธ|๐Ÿงš๐Ÿผโ€โ™€๏ธ|๐Ÿงš๐Ÿผโ€โ™‚๏ธ|๐Ÿงš๐Ÿฝโ€โ™€๏ธ|๐Ÿงš๐Ÿฝโ€โ™‚๏ธ|๐Ÿงš๐Ÿพโ€โ™€๏ธ|๐Ÿงš๐Ÿพโ€โ™‚๏ธ|๐Ÿงš๐Ÿฟโ€โ™€๏ธ|๐Ÿงš๐Ÿฟโ€โ™‚๏ธ|๐Ÿง›โ€โ™€๏ธ|๐Ÿง›โ€โ™‚๏ธ|๐Ÿง›๐Ÿปโ€โ™€๏ธ|๐Ÿง›๐Ÿปโ€โ™‚๏ธ|๐Ÿง›๐Ÿผโ€โ™€๏ธ|๐Ÿง›๐Ÿผโ€โ™‚๏ธ|๐Ÿง›๐Ÿฝโ€โ™€๏ธ|๐Ÿง›๐Ÿฝโ€โ™‚๏ธ|๐Ÿง›๐Ÿพโ€โ™€๏ธ|๐Ÿง›๐Ÿพโ€โ™‚๏ธ|๐Ÿง›๐Ÿฟโ€โ™€๏ธ|๐Ÿง›๐Ÿฟโ€โ™‚๏ธ|๐Ÿงœโ€โ™€๏ธ|๐Ÿงœโ€โ™‚๏ธ|๐Ÿงœ๐Ÿปโ€โ™€๏ธ|๐Ÿงœ๐Ÿปโ€โ™‚๏ธ|๐Ÿงœ๐Ÿผโ€โ™€๏ธ|๐Ÿงœ๐Ÿผโ€โ™‚๏ธ|๐Ÿงœ๐Ÿฝโ€โ™€๏ธ|๐Ÿงœ๐Ÿฝโ€โ™‚๏ธ|๐Ÿงœ๐Ÿพโ€โ™€๏ธ|๐Ÿงœ๐Ÿพโ€โ™‚๏ธ|๐Ÿงœ๐Ÿฟโ€โ™€๏ธ|๐Ÿงœ๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐Ÿงžโ€โ™€๏ธ|๐Ÿงžโ€โ™‚๏ธ|๐ŸงŸโ€โ™€๏ธ|๐ŸงŸโ€โ™‚๏ธ|๐Ÿ‘จโ€๐Ÿฆฐ|๐Ÿ‘จโ€๐Ÿฆฑ|๐Ÿ‘จโ€๐Ÿฆฒ|๐Ÿ‘จโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆณ|๐Ÿ‘ฉโ€๐Ÿฆฐ|๐Ÿ‘ฉโ€๐Ÿฆฑ|๐Ÿ‘ฉโ€๐Ÿฆฒ|๐Ÿ‘ฉโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆณ|๐Ÿง‘โ€๐Ÿฆฐ|๐Ÿง‘โ€๐Ÿฆฑ|๐Ÿง‘โ€๐Ÿฆฒ|๐Ÿง‘โ€๐Ÿฆณ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿปโ€๐Ÿฆณ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿผโ€๐Ÿฆณ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆณ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿพโ€๐Ÿฆณ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆณ|โค๏ธโ€๐Ÿ”ฅ|โค๏ธโ€๐Ÿฉน|๐Ÿณ๏ธโ€โšง๏ธ|๐Ÿณ๏ธโ€๐ŸŒˆ|๐Ÿดโ€โ˜ ๏ธ|๐Ÿˆโ€โฌ›|๐Ÿ•โ€๐Ÿฆบ|๐Ÿฆโ€โฌ›|๐Ÿปโ€โ„๏ธ|๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ|๐Ÿ˜ฎโ€๐Ÿ’จ|๐Ÿ˜ตโ€๐Ÿ’ซ|๐Ÿ˜ถโ€๐ŸŒซ๏ธ|๐Ÿง‘โ€๐ŸŽ„)/gu, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index dee635089e00..125e391f12d4 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -265,18 +265,30 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { + let str = `${text}`; const emojis = []; - if (!text) { + + if (!str) { return emojis; } - let parseEmojis = text.match(CONST.REGEX.EMOJIS); + let sequenceEmojis = str.match(CONST.REGEX.EMOJI_ZWJ_SEQUENCES); + + if (sequenceEmojis) { + sequenceEmojis = [...new Set(sequenceEmojis)]; + for (const sequenceEmoji of sequenceEmojis) { + const regex = new RegExp(sequenceEmoji, 'gu'); + str = str.replace(regex, ''); + } + } + + let parseEmojis = str.match(CONST.REGEX.EMOJIS); - if (!parseEmojis) { + if (!parseEmojis && !sequenceEmojis) { return emojis; } - parseEmojis = [...new Set(parseEmojis)]; + parseEmojis = [...new Set(parseEmojis), ...sequenceEmojis]; for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; From 4a533bd3b902e8134567f825d79e773a32ab80b6 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Wed, 26 Jul 2023 02:36:18 +0530 Subject: [PATCH 008/214] Removed extra emojis --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index c4fd7a5174eb..395967b43e0f 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1149,7 +1149,7 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, EMOJI_ZWJ_SEQUENCES: - /(๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘จโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ|๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€๐ŸŽ„|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€๐ŸŽ„|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€๐ŸŽ„|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€๐ŸŽ„|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐ŸŽ„|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿผโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿพ|๐Ÿซฑ๐Ÿฝโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿพโ€๐Ÿซฒ๐Ÿฟ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿป|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿผ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿฝ|๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿพ|๐Ÿ‘จโ€โš•๏ธ|๐Ÿ‘จโ€โš–๏ธ|๐Ÿ‘จโ€โœˆ๏ธ|๐Ÿ‘จโ€๐ŸŒพ|๐Ÿ‘จโ€๐Ÿณ|๐Ÿ‘จโ€๐Ÿผ|๐Ÿ‘จโ€๐ŸŽ“|๐Ÿ‘จโ€๐ŸŽค|๐Ÿ‘จโ€๐ŸŽจ|๐Ÿ‘จโ€๐Ÿซ|๐Ÿ‘จโ€๐Ÿญ|๐Ÿ‘จโ€๐Ÿ’ป|๐Ÿ‘จโ€๐Ÿ’ผ|๐Ÿ‘จโ€๐Ÿ”ง|๐Ÿ‘จโ€๐Ÿ”ฌ|๐Ÿ‘จโ€๐Ÿš€|๐Ÿ‘จโ€๐Ÿš’|๐Ÿ‘จโ€๐Ÿฆฏ|๐Ÿ‘จโ€๐Ÿฆผ|๐Ÿ‘จโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿปโ€โš•๏ธ|๐Ÿ‘จ๐Ÿปโ€โš–๏ธ|๐Ÿ‘จ๐Ÿปโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿปโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿปโ€๐Ÿณ|๐Ÿ‘จ๐Ÿปโ€๐Ÿผ|๐Ÿ‘จ๐Ÿปโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿปโ€๐ŸŽค|๐Ÿ‘จ๐Ÿปโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿปโ€๐Ÿซ|๐Ÿ‘จ๐Ÿปโ€๐Ÿญ|๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿปโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿปโ€๐Ÿš€|๐Ÿ‘จ๐Ÿปโ€๐Ÿš’|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿผโ€โš•๏ธ|๐Ÿ‘จ๐Ÿผโ€โš–๏ธ|๐Ÿ‘จ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿผโ€๐Ÿณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿผโ€๐ŸŽค|๐Ÿ‘จ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿผโ€๐Ÿซ|๐Ÿ‘จ๐Ÿผโ€๐Ÿญ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿผโ€๐Ÿš€|๐Ÿ‘จ๐Ÿผโ€๐Ÿš’|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿฝโ€โš•๏ธ|๐Ÿ‘จ๐Ÿฝโ€โš–๏ธ|๐Ÿ‘จ๐Ÿฝโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿฝโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿณ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽค|๐Ÿ‘จ๐Ÿฝโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿซ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿญ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿš€|๐Ÿ‘จ๐Ÿฝโ€๐Ÿš’|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿพโ€โš•๏ธ|๐Ÿ‘จ๐Ÿพโ€โš–๏ธ|๐Ÿ‘จ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿพโ€๐Ÿณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿพโ€๐ŸŽค|๐Ÿ‘จ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿพโ€๐Ÿซ|๐Ÿ‘จ๐Ÿพโ€๐Ÿญ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿพโ€๐Ÿš€|๐Ÿ‘จ๐Ÿพโ€๐Ÿš’|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿฟโ€โš•๏ธ|๐Ÿ‘จ๐Ÿฟโ€โš–๏ธ|๐Ÿ‘จ๐Ÿฟโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿฟโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿณ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿผ|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽค|๐Ÿ‘จ๐Ÿฟโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿซ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿญ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿš€|๐Ÿ‘จ๐Ÿฟโ€๐Ÿš’|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฝ|๐Ÿ‘ฉโ€โš•๏ธ|๐Ÿ‘ฉโ€โš–๏ธ|๐Ÿ‘ฉโ€โœˆ๏ธ|๐Ÿ‘ฉโ€๐ŸŒพ|๐Ÿ‘ฉโ€๐Ÿณ|๐Ÿ‘ฉโ€๐Ÿผ|๐Ÿ‘ฉโ€๐ŸŽ“|๐Ÿ‘ฉโ€๐ŸŽค|๐Ÿ‘ฉโ€๐ŸŽจ|๐Ÿ‘ฉโ€๐Ÿซ|๐Ÿ‘ฉโ€๐Ÿญ|๐Ÿ‘ฉโ€๐Ÿ’ป|๐Ÿ‘ฉโ€๐Ÿ’ผ|๐Ÿ‘ฉโ€๐Ÿ”ง|๐Ÿ‘ฉโ€๐Ÿ”ฌ|๐Ÿ‘ฉโ€๐Ÿš€|๐Ÿ‘ฉโ€๐Ÿš’|๐Ÿ‘ฉโ€๐Ÿฆฏ|๐Ÿ‘ฉโ€๐Ÿฆผ|๐Ÿ‘ฉโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿปโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿปโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿปโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿปโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿผโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿฝโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿฝโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿพโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿฟโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿฟโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฝ|๐Ÿง‘โ€โš•๏ธ|๐Ÿง‘โ€โš–๏ธ|๐Ÿง‘โ€โœˆ๏ธ|๐Ÿง‘โ€๐ŸŒพ|๐Ÿง‘โ€๐Ÿณ|๐Ÿง‘โ€๐Ÿผ|๐Ÿง‘โ€๐ŸŽ“|๐Ÿง‘โ€๐ŸŽค|๐Ÿง‘โ€๐ŸŽจ|๐Ÿง‘โ€๐Ÿซ|๐Ÿง‘โ€๐Ÿญ|๐Ÿง‘โ€๐Ÿ’ป|๐Ÿง‘โ€๐Ÿ’ผ|๐Ÿง‘โ€๐Ÿ”ง|๐Ÿง‘โ€๐Ÿ”ฌ|๐Ÿง‘โ€๐Ÿš€|๐Ÿง‘โ€๐Ÿš’|๐Ÿง‘โ€๐Ÿฆฏ|๐Ÿง‘โ€๐Ÿฆผ|๐Ÿง‘โ€๐Ÿฆฝ|๐Ÿง‘๐Ÿปโ€โš•๏ธ|๐Ÿง‘๐Ÿปโ€โš–๏ธ|๐Ÿง‘๐Ÿปโ€โœˆ๏ธ|๐Ÿง‘๐Ÿปโ€๐ŸŒพ|๐Ÿง‘๐Ÿปโ€๐Ÿณ|๐Ÿง‘๐Ÿปโ€๐Ÿผ|๐Ÿง‘๐Ÿปโ€๐ŸŽ“|๐Ÿง‘๐Ÿปโ€๐ŸŽค|๐Ÿง‘๐Ÿปโ€๐ŸŽจ|๐Ÿง‘๐Ÿปโ€๐Ÿซ|๐Ÿง‘๐Ÿปโ€๐Ÿญ|๐Ÿง‘๐Ÿปโ€๐Ÿ’ป|๐Ÿง‘๐Ÿปโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿปโ€๐Ÿ”ง|๐Ÿง‘๐Ÿปโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿปโ€๐Ÿš€|๐Ÿง‘๐Ÿปโ€๐Ÿš’|๐Ÿง‘๐Ÿปโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿปโ€๐Ÿฆผ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿผโ€โš•๏ธ|๐Ÿง‘๐Ÿผโ€โš–๏ธ|๐Ÿง‘๐Ÿผโ€โœˆ๏ธ|๐Ÿง‘๐Ÿผโ€๐ŸŒพ|๐Ÿง‘๐Ÿผโ€๐Ÿณ|๐Ÿง‘๐Ÿผโ€๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐ŸŽ“|๐Ÿง‘๐Ÿผโ€๐ŸŽค|๐Ÿง‘๐Ÿผโ€๐ŸŽจ|๐Ÿง‘๐Ÿผโ€๐Ÿซ|๐Ÿง‘๐Ÿผโ€๐Ÿญ|๐Ÿง‘๐Ÿผโ€๐Ÿ’ป|๐Ÿง‘๐Ÿผโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿผโ€๐Ÿ”ง|๐Ÿง‘๐Ÿผโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿผโ€๐Ÿš€|๐Ÿง‘๐Ÿผโ€๐Ÿš’|๐Ÿง‘๐Ÿผโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿผโ€๐Ÿฆผ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿฝโ€โš•๏ธ|๐Ÿง‘๐Ÿฝโ€โš–๏ธ|๐Ÿง‘๐Ÿฝโ€โœˆ๏ธ|๐Ÿง‘๐Ÿฝโ€๐ŸŒพ|๐Ÿง‘๐Ÿฝโ€๐Ÿณ|๐Ÿง‘๐Ÿฝโ€๐Ÿผ|๐Ÿง‘๐Ÿฝโ€๐ŸŽ“|๐Ÿง‘๐Ÿฝโ€๐ŸŽค|๐Ÿง‘๐Ÿฝโ€๐ŸŽจ|๐Ÿง‘๐Ÿฝโ€๐Ÿซ|๐Ÿง‘๐Ÿฝโ€๐Ÿญ|๐Ÿง‘๐Ÿฝโ€๐Ÿ’ป|๐Ÿง‘๐Ÿฝโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿฝโ€๐Ÿ”ง|๐Ÿง‘๐Ÿฝโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿฝโ€๐Ÿš€|๐Ÿง‘๐Ÿฝโ€๐Ÿš’|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆผ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿพโ€โš•๏ธ|๐Ÿง‘๐Ÿพโ€โš–๏ธ|๐Ÿง‘๐Ÿพโ€โœˆ๏ธ|๐Ÿง‘๐Ÿพโ€๐ŸŒพ|๐Ÿง‘๐Ÿพโ€๐Ÿณ|๐Ÿง‘๐Ÿพโ€๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐ŸŽ“|๐Ÿง‘๐Ÿพโ€๐ŸŽค|๐Ÿง‘๐Ÿพโ€๐ŸŽจ|๐Ÿง‘๐Ÿพโ€๐Ÿซ|๐Ÿง‘๐Ÿพโ€๐Ÿญ|๐Ÿง‘๐Ÿพโ€๐Ÿ’ป|๐Ÿง‘๐Ÿพโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿพโ€๐Ÿ”ง|๐Ÿง‘๐Ÿพโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿพโ€๐Ÿš€|๐Ÿง‘๐Ÿพโ€๐Ÿš’|๐Ÿง‘๐Ÿพโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿพโ€๐Ÿฆผ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿฟโ€โš•๏ธ|๐Ÿง‘๐Ÿฟโ€โš–๏ธ|๐Ÿง‘๐Ÿฟโ€โœˆ๏ธ|๐Ÿง‘๐Ÿฟโ€๐ŸŒพ|๐Ÿง‘๐Ÿฟโ€๐Ÿณ|๐Ÿง‘๐Ÿฟโ€๐Ÿผ|๐Ÿง‘๐Ÿฟโ€๐ŸŽ“|๐Ÿง‘๐Ÿฟโ€๐ŸŽค|๐Ÿง‘๐Ÿฟโ€๐ŸŽจ|๐Ÿง‘๐Ÿฟโ€๐Ÿซ|๐Ÿง‘๐Ÿฟโ€๐Ÿญ|๐Ÿง‘๐Ÿฟโ€๐Ÿ’ป|๐Ÿง‘๐Ÿฟโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿฟโ€๐Ÿ”ง|๐Ÿง‘๐Ÿฟโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿฟโ€๐Ÿš€|๐Ÿง‘๐Ÿฟโ€๐Ÿš’|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆผ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฝ|โ›น๐Ÿปโ€โ™€๏ธ|โ›น๐Ÿปโ€โ™‚๏ธ|โ›น๐Ÿผโ€โ™€๏ธ|โ›น๐Ÿผโ€โ™‚๏ธ|โ›น๐Ÿฝโ€โ™€๏ธ|โ›น๐Ÿฝโ€โ™‚๏ธ|โ›น๐Ÿพโ€โ™€๏ธ|โ›น๐Ÿพโ€โ™‚๏ธ|โ›น๐Ÿฟโ€โ™€๏ธ|โ›น๐Ÿฟโ€โ™‚๏ธ|โ›น๏ธโ€โ™€๏ธ|โ›น๏ธโ€โ™‚๏ธ|๐Ÿƒโ€โ™€๏ธ|๐Ÿƒโ€โ™‚๏ธ|๐Ÿƒ๐Ÿปโ€โ™€๏ธ|๐Ÿƒ๐Ÿปโ€โ™‚๏ธ|๐Ÿƒ๐Ÿผโ€โ™€๏ธ|๐Ÿƒ๐Ÿผโ€โ™‚๏ธ|๐Ÿƒ๐Ÿฝโ€โ™€๏ธ|๐Ÿƒ๐Ÿฝโ€โ™‚๏ธ|๐Ÿƒ๐Ÿพโ€โ™€๏ธ|๐Ÿƒ๐Ÿพโ€โ™‚๏ธ|๐Ÿƒ๐Ÿฟโ€โ™€๏ธ|๐Ÿƒ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ„โ€โ™€๏ธ|๐Ÿ„โ€โ™‚๏ธ|๐Ÿ„๐Ÿปโ€โ™€๏ธ|๐Ÿ„๐Ÿปโ€โ™‚๏ธ|๐Ÿ„๐Ÿผโ€โ™€๏ธ|๐Ÿ„๐Ÿผโ€โ™‚๏ธ|๐Ÿ„๐Ÿฝโ€โ™€๏ธ|๐Ÿ„๐Ÿฝโ€โ™‚๏ธ|๐Ÿ„๐Ÿพโ€โ™€๏ธ|๐Ÿ„๐Ÿพโ€โ™‚๏ธ|๐Ÿ„๐Ÿฟโ€โ™€๏ธ|๐Ÿ„๐Ÿฟโ€โ™‚๏ธ|๐ŸŠโ€โ™€๏ธ|๐ŸŠโ€โ™‚๏ธ|๐ŸŠ๐Ÿปโ€โ™€๏ธ|๐ŸŠ๐Ÿปโ€โ™‚๏ธ|๐ŸŠ๐Ÿผโ€โ™€๏ธ|๐ŸŠ๐Ÿผโ€โ™‚๏ธ|๐ŸŠ๐Ÿฝโ€โ™€๏ธ|๐ŸŠ๐Ÿฝโ€โ™‚๏ธ|๐ŸŠ๐Ÿพโ€โ™€๏ธ|๐ŸŠ๐Ÿพโ€โ™‚๏ธ|๐ŸŠ๐Ÿฟโ€โ™€๏ธ|๐ŸŠ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‹๐Ÿปโ€โ™€๏ธ|๐Ÿ‹๐Ÿปโ€โ™‚๏ธ|๐Ÿ‹๐Ÿผโ€โ™€๏ธ|๐Ÿ‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ‹๐Ÿฝโ€โ™€๏ธ|๐Ÿ‹๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‹๐Ÿพโ€โ™€๏ธ|๐Ÿ‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ‹๐Ÿฟโ€โ™€๏ธ|๐Ÿ‹๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‹๏ธโ€โ™€๏ธ|๐Ÿ‹๏ธโ€โ™‚๏ธ|๐ŸŒ๐Ÿปโ€โ™€๏ธ|๐ŸŒ๐Ÿปโ€โ™‚๏ธ|๐ŸŒ๐Ÿผโ€โ™€๏ธ|๐ŸŒ๐Ÿผโ€โ™‚๏ธ|๐ŸŒ๐Ÿฝโ€โ™€๏ธ|๐ŸŒ๐Ÿฝโ€โ™‚๏ธ|๐ŸŒ๐Ÿพโ€โ™€๏ธ|๐ŸŒ๐Ÿพโ€โ™‚๏ธ|๐ŸŒ๐Ÿฟโ€โ™€๏ธ|๐ŸŒ๐Ÿฟโ€โ™‚๏ธ|๐ŸŒ๏ธโ€โ™€๏ธ|๐ŸŒ๏ธโ€โ™‚๏ธ|๐Ÿ‘ฎโ€โ™€๏ธ|๐Ÿ‘ฎโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ฏโ€โ™€๏ธ|๐Ÿ‘ฏโ€โ™‚๏ธ|๐Ÿ‘ฐโ€โ™€๏ธ|๐Ÿ‘ฐโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ฑโ€โ™€๏ธ|๐Ÿ‘ฑโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ณโ€โ™€๏ธ|๐Ÿ‘ณโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿฟโ€โ™‚๏ธ|๐Ÿ‘ทโ€โ™€๏ธ|๐Ÿ‘ทโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿปโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿปโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿฝโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿฝโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿฟโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’โ€โ™€๏ธ|๐Ÿ’โ€โ™‚๏ธ|๐Ÿ’๐Ÿปโ€โ™€๏ธ|๐Ÿ’๐Ÿปโ€โ™‚๏ธ|๐Ÿ’๐Ÿผโ€โ™€๏ธ|๐Ÿ’๐Ÿผโ€โ™‚๏ธ|๐Ÿ’๐Ÿฝโ€โ™€๏ธ|๐Ÿ’๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’๐Ÿพโ€โ™€๏ธ|๐Ÿ’๐Ÿพโ€โ™‚๏ธ|๐Ÿ’๐Ÿฟโ€โ™€๏ธ|๐Ÿ’๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’‚โ€โ™€๏ธ|๐Ÿ’‚โ€โ™‚๏ธ|๐Ÿ’‚๐Ÿปโ€โ™€๏ธ|๐Ÿ’‚๐Ÿปโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿผโ€โ™€๏ธ|๐Ÿ’‚๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿฝโ€โ™€๏ธ|๐Ÿ’‚๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿพโ€โ™€๏ธ|๐Ÿ’‚๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿฟโ€โ™€๏ธ|๐Ÿ’‚๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’†โ€โ™€๏ธ|๐Ÿ’†โ€โ™‚๏ธ|๐Ÿ’†๐Ÿปโ€โ™€๏ธ|๐Ÿ’†๐Ÿปโ€โ™‚๏ธ|๐Ÿ’†๐Ÿผโ€โ™€๏ธ|๐Ÿ’†๐Ÿผโ€โ™‚๏ธ|๐Ÿ’†๐Ÿฝโ€โ™€๏ธ|๐Ÿ’†๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’†๐Ÿพโ€โ™€๏ธ|๐Ÿ’†๐Ÿพโ€โ™‚๏ธ|๐Ÿ’†๐Ÿฟโ€โ™€๏ธ|๐Ÿ’†๐Ÿฟโ€โ™‚๏ธ|๐Ÿ’‡โ€โ™€๏ธ|๐Ÿ’‡โ€โ™‚๏ธ|๐Ÿ’‡๐Ÿปโ€โ™€๏ธ|๐Ÿ’‡๐Ÿปโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿผโ€โ™€๏ธ|๐Ÿ’‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿฝโ€โ™€๏ธ|๐Ÿ’‡๐Ÿฝโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿพโ€โ™€๏ธ|๐Ÿ’‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿฟโ€โ™€๏ธ|๐Ÿ’‡๐Ÿฟโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿปโ€โ™€๏ธ|๐Ÿ•ต๐Ÿปโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿผโ€โ™€๏ธ|๐Ÿ•ต๐Ÿผโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿฝโ€โ™€๏ธ|๐Ÿ•ต๐Ÿฝโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿพโ€โ™€๏ธ|๐Ÿ•ต๐Ÿพโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿฟโ€โ™€๏ธ|๐Ÿ•ต๐Ÿฟโ€โ™‚๏ธ|๐Ÿ•ต๏ธโ€โ™€๏ธ|๐Ÿ•ต๏ธโ€โ™‚๏ธ|๐Ÿ™…โ€โ™€๏ธ|๐Ÿ™…โ€โ™‚๏ธ|๐Ÿ™…๐Ÿปโ€โ™€๏ธ|๐Ÿ™…๐Ÿปโ€โ™‚๏ธ|๐Ÿ™…๐Ÿผโ€โ™€๏ธ|๐Ÿ™…๐Ÿผโ€โ™‚๏ธ|๐Ÿ™…๐Ÿฝโ€โ™€๏ธ|๐Ÿ™…๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™…๐Ÿพโ€โ™€๏ธ|๐Ÿ™…๐Ÿพโ€โ™‚๏ธ|๐Ÿ™…๐Ÿฟโ€โ™€๏ธ|๐Ÿ™…๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™†โ€โ™€๏ธ|๐Ÿ™†โ€โ™‚๏ธ|๐Ÿ™†๐Ÿปโ€โ™€๏ธ|๐Ÿ™†๐Ÿปโ€โ™‚๏ธ|๐Ÿ™†๐Ÿผโ€โ™€๏ธ|๐Ÿ™†๐Ÿผโ€โ™‚๏ธ|๐Ÿ™†๐Ÿฝโ€โ™€๏ธ|๐Ÿ™†๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™†๐Ÿพโ€โ™€๏ธ|๐Ÿ™†๐Ÿพโ€โ™‚๏ธ|๐Ÿ™†๐Ÿฟโ€โ™€๏ธ|๐Ÿ™†๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™‡โ€โ™€๏ธ|๐Ÿ™‡โ€โ™‚๏ธ|๐Ÿ™‡๐Ÿปโ€โ™€๏ธ|๐Ÿ™‡๐Ÿปโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿผโ€โ™€๏ธ|๐Ÿ™‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿฝโ€โ™€๏ธ|๐Ÿ™‡๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿพโ€โ™€๏ธ|๐Ÿ™‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿฟโ€โ™€๏ธ|๐Ÿ™‡๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™‹โ€โ™€๏ธ|๐Ÿ™‹โ€โ™‚๏ธ|๐Ÿ™‹๐Ÿปโ€โ™€๏ธ|๐Ÿ™‹๐Ÿปโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿผโ€โ™€๏ธ|๐Ÿ™‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿฝโ€โ™€๏ธ|๐Ÿ™‹๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿพโ€โ™€๏ธ|๐Ÿ™‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿฟโ€โ™€๏ธ|๐Ÿ™‹๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™โ€โ™€๏ธ|๐Ÿ™โ€โ™‚๏ธ|๐Ÿ™๐Ÿปโ€โ™€๏ธ|๐Ÿ™๐Ÿปโ€โ™‚๏ธ|๐Ÿ™๐Ÿผโ€โ™€๏ธ|๐Ÿ™๐Ÿผโ€โ™‚๏ธ|๐Ÿ™๐Ÿฝโ€โ™€๏ธ|๐Ÿ™๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™๐Ÿพโ€โ™€๏ธ|๐Ÿ™๐Ÿพโ€โ™‚๏ธ|๐Ÿ™๐Ÿฟโ€โ™€๏ธ|๐Ÿ™๐Ÿฟโ€โ™‚๏ธ|๐Ÿ™Žโ€โ™€๏ธ|๐Ÿ™Žโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿปโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿปโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿฝโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿฝโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿฟโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿฟโ€โ™‚๏ธ|๐Ÿšฃโ€โ™€๏ธ|๐Ÿšฃโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿปโ€โ™€๏ธ|๐Ÿšฃ๐Ÿปโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿผโ€โ™€๏ธ|๐Ÿšฃ๐Ÿผโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿฝโ€โ™€๏ธ|๐Ÿšฃ๐Ÿฝโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿพโ€โ™€๏ธ|๐Ÿšฃ๐Ÿพโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿฟโ€โ™€๏ธ|๐Ÿšฃ๐Ÿฟโ€โ™‚๏ธ|๐Ÿšดโ€โ™€๏ธ|๐Ÿšดโ€โ™‚๏ธ|๐Ÿšด๐Ÿปโ€โ™€๏ธ|๐Ÿšด๐Ÿปโ€โ™‚๏ธ|๐Ÿšด๐Ÿผโ€โ™€๏ธ|๐Ÿšด๐Ÿผโ€โ™‚๏ธ|๐Ÿšด๐Ÿฝโ€โ™€๏ธ|๐Ÿšด๐Ÿฝโ€โ™‚๏ธ|๐Ÿšด๐Ÿพโ€โ™€๏ธ|๐Ÿšด๐Ÿพโ€โ™‚๏ธ|๐Ÿšด๐Ÿฟโ€โ™€๏ธ|๐Ÿšด๐Ÿฟโ€โ™‚๏ธ|๐Ÿšตโ€โ™€๏ธ|๐Ÿšตโ€โ™‚๏ธ|๐Ÿšต๐Ÿปโ€โ™€๏ธ|๐Ÿšต๐Ÿปโ€โ™‚๏ธ|๐Ÿšต๐Ÿผโ€โ™€๏ธ|๐Ÿšต๐Ÿผโ€โ™‚๏ธ|๐Ÿšต๐Ÿฝโ€โ™€๏ธ|๐Ÿšต๐Ÿฝโ€โ™‚๏ธ|๐Ÿšต๐Ÿพโ€โ™€๏ธ|๐Ÿšต๐Ÿพโ€โ™‚๏ธ|๐Ÿšต๐Ÿฟโ€โ™€๏ธ|๐Ÿšต๐Ÿฟโ€โ™‚๏ธ|๐Ÿšถโ€โ™€๏ธ|๐Ÿšถโ€โ™‚๏ธ|๐Ÿšถ๐Ÿปโ€โ™€๏ธ|๐Ÿšถ๐Ÿปโ€โ™‚๏ธ|๐Ÿšถ๐Ÿผโ€โ™€๏ธ|๐Ÿšถ๐Ÿผโ€โ™‚๏ธ|๐Ÿšถ๐Ÿฝโ€โ™€๏ธ|๐Ÿšถ๐Ÿฝโ€โ™‚๏ธ|๐Ÿšถ๐Ÿพโ€โ™€๏ธ|๐Ÿšถ๐Ÿพโ€โ™‚๏ธ|๐Ÿšถ๐Ÿฟโ€โ™€๏ธ|๐Ÿšถ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคฆโ€โ™€๏ธ|๐Ÿคฆโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿปโ€โ™€๏ธ|๐Ÿคฆ๐Ÿปโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿผโ€โ™€๏ธ|๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿฝโ€โ™€๏ธ|๐Ÿคฆ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿพโ€โ™€๏ธ|๐Ÿคฆ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿฟโ€โ™€๏ธ|๐Ÿคฆ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคตโ€โ™€๏ธ|๐Ÿคตโ€โ™‚๏ธ|๐Ÿคต๐Ÿปโ€โ™€๏ธ|๐Ÿคต๐Ÿปโ€โ™‚๏ธ|๐Ÿคต๐Ÿผโ€โ™€๏ธ|๐Ÿคต๐Ÿผโ€โ™‚๏ธ|๐Ÿคต๐Ÿฝโ€โ™€๏ธ|๐Ÿคต๐Ÿฝโ€โ™‚๏ธ|๐Ÿคต๐Ÿพโ€โ™€๏ธ|๐Ÿคต๐Ÿพโ€โ™‚๏ธ|๐Ÿคต๐Ÿฟโ€โ™€๏ธ|๐Ÿคต๐Ÿฟโ€โ™‚๏ธ|๐Ÿคทโ€โ™€๏ธ|๐Ÿคทโ€โ™‚๏ธ|๐Ÿคท๐Ÿปโ€โ™€๏ธ|๐Ÿคท๐Ÿปโ€โ™‚๏ธ|๐Ÿคท๐Ÿผโ€โ™€๏ธ|๐Ÿคท๐Ÿผโ€โ™‚๏ธ|๐Ÿคท๐Ÿฝโ€โ™€๏ธ|๐Ÿคท๐Ÿฝโ€โ™‚๏ธ|๐Ÿคท๐Ÿพโ€โ™€๏ธ|๐Ÿคท๐Ÿพโ€โ™‚๏ธ|๐Ÿคท๐Ÿฟโ€โ™€๏ธ|๐Ÿคท๐Ÿฟโ€โ™‚๏ธ|๐Ÿคธโ€โ™€๏ธ|๐Ÿคธโ€โ™‚๏ธ|๐Ÿคธ๐Ÿปโ€โ™€๏ธ|๐Ÿคธ๐Ÿปโ€โ™‚๏ธ|๐Ÿคธ๐Ÿผโ€โ™€๏ธ|๐Ÿคธ๐Ÿผโ€โ™‚๏ธ|๐Ÿคธ๐Ÿฝโ€โ™€๏ธ|๐Ÿคธ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคธ๐Ÿพโ€โ™€๏ธ|๐Ÿคธ๐Ÿพโ€โ™‚๏ธ|๐Ÿคธ๐Ÿฟโ€โ™€๏ธ|๐Ÿคธ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคนโ€โ™€๏ธ|๐Ÿคนโ€โ™‚๏ธ|๐Ÿคน๐Ÿปโ€โ™€๏ธ|๐Ÿคน๐Ÿปโ€โ™‚๏ธ|๐Ÿคน๐Ÿผโ€โ™€๏ธ|๐Ÿคน๐Ÿผโ€โ™‚๏ธ|๐Ÿคน๐Ÿฝโ€โ™€๏ธ|๐Ÿคน๐Ÿฝโ€โ™‚๏ธ|๐Ÿคน๐Ÿพโ€โ™€๏ธ|๐Ÿคน๐Ÿพโ€โ™‚๏ธ|๐Ÿคน๐Ÿฟโ€โ™€๏ธ|๐Ÿคน๐Ÿฟโ€โ™‚๏ธ|๐Ÿคผโ€โ™€๏ธ|๐Ÿคผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™€๏ธ|๐Ÿคฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿปโ€โ™€๏ธ|๐Ÿคฝ๐Ÿปโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿผโ€โ™€๏ธ|๐Ÿคฝ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿฝโ€โ™€๏ธ|๐Ÿคฝ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿพโ€โ™€๏ธ|๐Ÿคฝ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿฟโ€โ™€๏ธ|๐Ÿคฝ๐Ÿฟโ€โ™‚๏ธ|๐Ÿคพโ€โ™€๏ธ|๐Ÿคพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿปโ€โ™€๏ธ|๐Ÿคพ๐Ÿปโ€โ™‚๏ธ|๐Ÿคพ๐Ÿผโ€โ™€๏ธ|๐Ÿคพ๐Ÿผโ€โ™‚๏ธ|๐Ÿคพ๐Ÿฝโ€โ™€๏ธ|๐Ÿคพ๐Ÿฝโ€โ™‚๏ธ|๐Ÿคพ๐Ÿพโ€โ™€๏ธ|๐Ÿคพ๐Ÿพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿฟโ€โ™€๏ธ|๐Ÿคพ๐Ÿฟโ€โ™‚๏ธ|๐Ÿฆธโ€โ™€๏ธ|๐Ÿฆธโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿปโ€โ™€๏ธ|๐Ÿฆธ๐Ÿปโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿผโ€โ™€๏ธ|๐Ÿฆธ๐Ÿผโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿฝโ€โ™€๏ธ|๐Ÿฆธ๐Ÿฝโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿพโ€โ™€๏ธ|๐Ÿฆธ๐Ÿพโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿฟโ€โ™€๏ธ|๐Ÿฆธ๐Ÿฟโ€โ™‚๏ธ|๐Ÿฆนโ€โ™€๏ธ|๐Ÿฆนโ€โ™‚๏ธ|๐Ÿฆน๐Ÿปโ€โ™€๏ธ|๐Ÿฆน๐Ÿปโ€โ™‚๏ธ|๐Ÿฆน๐Ÿผโ€โ™€๏ธ|๐Ÿฆน๐Ÿผโ€โ™‚๏ธ|๐Ÿฆน๐Ÿฝโ€โ™€๏ธ|๐Ÿฆน๐Ÿฝโ€โ™‚๏ธ|๐Ÿฆน๐Ÿพโ€โ™€๏ธ|๐Ÿฆน๐Ÿพโ€โ™‚๏ธ|๐Ÿฆน๐Ÿฟโ€โ™€๏ธ|๐Ÿฆน๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐ŸงŽโ€โ™€๏ธ|๐ŸงŽโ€โ™‚๏ธ|๐ŸงŽ๐Ÿปโ€โ™€๏ธ|๐ŸงŽ๐Ÿปโ€โ™‚๏ธ|๐ŸงŽ๐Ÿผโ€โ™€๏ธ|๐ŸงŽ๐Ÿผโ€โ™‚๏ธ|๐ŸงŽ๐Ÿฝโ€โ™€๏ธ|๐ŸงŽ๐Ÿฝโ€โ™‚๏ธ|๐ŸงŽ๐Ÿพโ€โ™€๏ธ|๐ŸงŽ๐Ÿพโ€โ™‚๏ธ|๐ŸงŽ๐Ÿฟโ€โ™€๏ธ|๐ŸงŽ๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐Ÿง”โ€โ™€๏ธ|๐Ÿง”โ€โ™‚๏ธ|๐Ÿง”๐Ÿปโ€โ™€๏ธ|๐Ÿง”๐Ÿปโ€โ™‚๏ธ|๐Ÿง”๐Ÿผโ€โ™€๏ธ|๐Ÿง”๐Ÿผโ€โ™‚๏ธ|๐Ÿง”๐Ÿฝโ€โ™€๏ธ|๐Ÿง”๐Ÿฝโ€โ™‚๏ธ|๐Ÿง”๐Ÿพโ€โ™€๏ธ|๐Ÿง”๐Ÿพโ€โ™‚๏ธ|๐Ÿง”๐Ÿฟโ€โ™€๏ธ|๐Ÿง”๐Ÿฟโ€โ™‚๏ธ|๐Ÿง–โ€โ™€๏ธ|๐Ÿง–โ€โ™‚๏ธ|๐Ÿง–๐Ÿปโ€โ™€๏ธ|๐Ÿง–๐Ÿปโ€โ™‚๏ธ|๐Ÿง–๐Ÿผโ€โ™€๏ธ|๐Ÿง–๐Ÿผโ€โ™‚๏ธ|๐Ÿง–๐Ÿฝโ€โ™€๏ธ|๐Ÿง–๐Ÿฝโ€โ™‚๏ธ|๐Ÿง–๐Ÿพโ€โ™€๏ธ|๐Ÿง–๐Ÿพโ€โ™‚๏ธ|๐Ÿง–๐Ÿฟโ€โ™€๏ธ|๐Ÿง–๐Ÿฟโ€โ™‚๏ธ|๐Ÿง—โ€โ™€๏ธ|๐Ÿง—โ€โ™‚๏ธ|๐Ÿง—๐Ÿปโ€โ™€๏ธ|๐Ÿง—๐Ÿปโ€โ™‚๏ธ|๐Ÿง—๐Ÿผโ€โ™€๏ธ|๐Ÿง—๐Ÿผโ€โ™‚๏ธ|๐Ÿง—๐Ÿฝโ€โ™€๏ธ|๐Ÿง—๐Ÿฝโ€โ™‚๏ธ|๐Ÿง—๐Ÿพโ€โ™€๏ธ|๐Ÿง—๐Ÿพโ€โ™‚๏ธ|๐Ÿง—๐Ÿฟโ€โ™€๏ธ|๐Ÿง—๐Ÿฟโ€โ™‚๏ธ|๐Ÿง˜โ€โ™€๏ธ|๐Ÿง˜โ€โ™‚๏ธ|๐Ÿง˜๐Ÿปโ€โ™€๏ธ|๐Ÿง˜๐Ÿปโ€โ™‚๏ธ|๐Ÿง˜๐Ÿผโ€โ™€๏ธ|๐Ÿง˜๐Ÿผโ€โ™‚๏ธ|๐Ÿง˜๐Ÿฝโ€โ™€๏ธ|๐Ÿง˜๐Ÿฝโ€โ™‚๏ธ|๐Ÿง˜๐Ÿพโ€โ™€๏ธ|๐Ÿง˜๐Ÿพโ€โ™‚๏ธ|๐Ÿง˜๐Ÿฟโ€โ™€๏ธ|๐Ÿง˜๐Ÿฟโ€โ™‚๏ธ|๐Ÿง™โ€โ™€๏ธ|๐Ÿง™โ€โ™‚๏ธ|๐Ÿง™๐Ÿปโ€โ™€๏ธ|๐Ÿง™๐Ÿปโ€โ™‚๏ธ|๐Ÿง™๐Ÿผโ€โ™€๏ธ|๐Ÿง™๐Ÿผโ€โ™‚๏ธ|๐Ÿง™๐Ÿฝโ€โ™€๏ธ|๐Ÿง™๐Ÿฝโ€โ™‚๏ธ|๐Ÿง™๐Ÿพโ€โ™€๏ธ|๐Ÿง™๐Ÿพโ€โ™‚๏ธ|๐Ÿง™๐Ÿฟโ€โ™€๏ธ|๐Ÿง™๐Ÿฟโ€โ™‚๏ธ|๐Ÿงšโ€โ™€๏ธ|๐Ÿงšโ€โ™‚๏ธ|๐Ÿงš๐Ÿปโ€โ™€๏ธ|๐Ÿงš๐Ÿปโ€โ™‚๏ธ|๐Ÿงš๐Ÿผโ€โ™€๏ธ|๐Ÿงš๐Ÿผโ€โ™‚๏ธ|๐Ÿงš๐Ÿฝโ€โ™€๏ธ|๐Ÿงš๐Ÿฝโ€โ™‚๏ธ|๐Ÿงš๐Ÿพโ€โ™€๏ธ|๐Ÿงš๐Ÿพโ€โ™‚๏ธ|๐Ÿงš๐Ÿฟโ€โ™€๏ธ|๐Ÿงš๐Ÿฟโ€โ™‚๏ธ|๐Ÿง›โ€โ™€๏ธ|๐Ÿง›โ€โ™‚๏ธ|๐Ÿง›๐Ÿปโ€โ™€๏ธ|๐Ÿง›๐Ÿปโ€โ™‚๏ธ|๐Ÿง›๐Ÿผโ€โ™€๏ธ|๐Ÿง›๐Ÿผโ€โ™‚๏ธ|๐Ÿง›๐Ÿฝโ€โ™€๏ธ|๐Ÿง›๐Ÿฝโ€โ™‚๏ธ|๐Ÿง›๐Ÿพโ€โ™€๏ธ|๐Ÿง›๐Ÿพโ€โ™‚๏ธ|๐Ÿง›๐Ÿฟโ€โ™€๏ธ|๐Ÿง›๐Ÿฟโ€โ™‚๏ธ|๐Ÿงœโ€โ™€๏ธ|๐Ÿงœโ€โ™‚๏ธ|๐Ÿงœ๐Ÿปโ€โ™€๏ธ|๐Ÿงœ๐Ÿปโ€โ™‚๏ธ|๐Ÿงœ๐Ÿผโ€โ™€๏ธ|๐Ÿงœ๐Ÿผโ€โ™‚๏ธ|๐Ÿงœ๐Ÿฝโ€โ™€๏ธ|๐Ÿงœ๐Ÿฝโ€โ™‚๏ธ|๐Ÿงœ๐Ÿพโ€โ™€๏ธ|๐Ÿงœ๐Ÿพโ€โ™‚๏ธ|๐Ÿงœ๐Ÿฟโ€โ™€๏ธ|๐Ÿงœ๐Ÿฟโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿปโ€โ™€๏ธ|๐Ÿง๐Ÿปโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿง๐Ÿฝโ€โ™€๏ธ|๐Ÿง๐Ÿฝโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿฟโ€โ™€๏ธ|๐Ÿง๐Ÿฟโ€โ™‚๏ธ|๐Ÿงžโ€โ™€๏ธ|๐Ÿงžโ€โ™‚๏ธ|๐ŸงŸโ€โ™€๏ธ|๐ŸงŸโ€โ™‚๏ธ|๐Ÿ‘จโ€๐Ÿฆฐ|๐Ÿ‘จโ€๐Ÿฆฑ|๐Ÿ‘จโ€๐Ÿฆฒ|๐Ÿ‘จโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿปโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿฆณ|๐Ÿ‘ฉโ€๐Ÿฆฐ|๐Ÿ‘ฉโ€๐Ÿฆฑ|๐Ÿ‘ฉโ€๐Ÿฆฒ|๐Ÿ‘ฉโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿฆณ|๐Ÿง‘โ€๐Ÿฆฐ|๐Ÿง‘โ€๐Ÿฆฑ|๐Ÿง‘โ€๐Ÿฆฒ|๐Ÿง‘โ€๐Ÿฆณ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿปโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿปโ€๐Ÿฆณ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿผโ€๐Ÿฆณ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿฝโ€๐Ÿฆณ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿพโ€๐Ÿฆณ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿฟโ€๐Ÿฆณ|โค๏ธโ€๐Ÿ”ฅ|โค๏ธโ€๐Ÿฉน|๐Ÿณ๏ธโ€โšง๏ธ|๐Ÿณ๏ธโ€๐ŸŒˆ|๐Ÿดโ€โ˜ ๏ธ|๐Ÿˆโ€โฌ›|๐Ÿ•โ€๐Ÿฆบ|๐Ÿฆโ€โฌ›|๐Ÿปโ€โ„๏ธ|๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ|๐Ÿ˜ฎโ€๐Ÿ’จ|๐Ÿ˜ตโ€๐Ÿ’ซ|๐Ÿ˜ถโ€๐ŸŒซ๏ธ|๐Ÿง‘โ€๐ŸŽ„)/gu, + /(๐Ÿ˜ถโ€๐ŸŒซ๏ธ|๐Ÿ˜ฎโ€๐Ÿ’จ|๐Ÿ˜ตโ€๐Ÿ’ซ|โค๏ธโ€๐Ÿ”ฅ|๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ|๐Ÿง”โ€โ™‚๏ธ|๐Ÿง”๐Ÿพโ€โ™‚๏ธ|๐Ÿง”๐Ÿผโ€โ™‚๏ธ|๐Ÿง”โ€โ™€๏ธ|๐Ÿง”๐Ÿพโ€โ™€๏ธ|๐Ÿง”๐Ÿผโ€โ™€๏ธ|๐Ÿ‘จโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘จโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘จโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘จโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฉโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฐ|๐Ÿง‘โ€๐Ÿฆฐ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘ฉโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฑ|๐Ÿง‘โ€๐Ÿฆฑ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘ฉโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆณ|๐Ÿง‘โ€๐Ÿฆณ|๐Ÿง‘๐Ÿพโ€๐Ÿฆณ|๐Ÿง‘๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘ฉโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฒ|๐Ÿง‘โ€๐Ÿฆฒ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฑโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฑโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™‚๏ธ|๐Ÿ™โ€โ™‚๏ธ|๐Ÿ™๐Ÿพโ€โ™‚๏ธ|๐Ÿ™๐Ÿผโ€โ™‚๏ธ|๐Ÿ™โ€โ™€๏ธ|๐Ÿ™๐Ÿพโ€โ™€๏ธ|๐Ÿ™๐Ÿผโ€โ™€๏ธ|๐Ÿ™Žโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™‚๏ธ|๐Ÿ™Žโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™€๏ธ|๐Ÿ™…โ€โ™‚๏ธ|๐Ÿ™…๐Ÿพโ€โ™‚๏ธ|๐Ÿ™…๐Ÿผโ€โ™‚๏ธ|๐Ÿ™…โ€โ™€๏ธ|๐Ÿ™…๐Ÿพโ€โ™€๏ธ|๐Ÿ™…๐Ÿผโ€โ™€๏ธ|๐Ÿ™†โ€โ™‚๏ธ|๐Ÿ™†๐Ÿพโ€โ™‚๏ธ|๐Ÿ™†๐Ÿผโ€โ™‚๏ธ|๐Ÿ™†โ€โ™€๏ธ|๐Ÿ™†๐Ÿพโ€โ™€๏ธ|๐Ÿ™†๐Ÿผโ€โ™€๏ธ|๐Ÿ’โ€โ™‚๏ธ|๐Ÿ’๐Ÿพโ€โ™‚๏ธ|๐Ÿ’๐Ÿผโ€โ™‚๏ธ|๐Ÿ’โ€โ™€๏ธ|๐Ÿ’๐Ÿพโ€โ™€๏ธ|๐Ÿ’๐Ÿผโ€โ™€๏ธ|๐Ÿ™‹โ€โ™‚๏ธ|๐Ÿ™‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‹โ€โ™€๏ธ|๐Ÿ™‹๐Ÿพโ€โ™€๏ธ|๐Ÿ™‹๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿ™‡โ€โ™‚๏ธ|๐Ÿ™‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‡โ€โ™€๏ธ|๐Ÿ™‡๐Ÿพโ€โ™€๏ธ|๐Ÿ™‡๐Ÿผโ€โ™€๏ธ|๐Ÿคฆโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฆโ€โ™€๏ธ|๐Ÿคฆ๐Ÿพโ€โ™€๏ธ|๐Ÿคฆ๐Ÿผโ€โ™€๏ธ|๐Ÿคทโ€โ™‚๏ธ|๐Ÿคท๐Ÿพโ€โ™‚๏ธ|๐Ÿคท๐Ÿผโ€โ™‚๏ธ|๐Ÿคทโ€โ™€๏ธ|๐Ÿคท๐Ÿพโ€โ™€๏ธ|๐Ÿคท๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€โš•๏ธ|๐Ÿง‘๐Ÿพโ€โš•๏ธ|๐Ÿง‘๐Ÿผโ€โš•๏ธ|๐Ÿ‘จโ€โš•๏ธ|๐Ÿ‘จ๐Ÿพโ€โš•๏ธ|๐Ÿ‘จ๐Ÿผโ€โš•๏ธ|๐Ÿ‘ฉโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš•๏ธ|๐Ÿง‘โ€๐ŸŽ“|๐Ÿง‘๐Ÿพโ€๐ŸŽ“|๐Ÿง‘๐Ÿผโ€๐ŸŽ“|๐Ÿ‘จโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘ฉโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽ“|๐Ÿง‘โ€๐Ÿซ|๐Ÿง‘๐Ÿพโ€๐Ÿซ|๐Ÿง‘๐Ÿผโ€๐Ÿซ|๐Ÿ‘จโ€๐Ÿซ|๐Ÿ‘จ๐Ÿพโ€๐Ÿซ|๐Ÿ‘จ๐Ÿผโ€๐Ÿซ|๐Ÿ‘ฉโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ|๐Ÿง‘โ€โš–๏ธ|๐Ÿง‘๐Ÿพโ€โš–๏ธ|๐Ÿง‘๐Ÿผโ€โš–๏ธ|๐Ÿ‘จโ€โš–๏ธ|๐Ÿ‘จ๐Ÿพโ€โš–๏ธ|๐Ÿ‘จ๐Ÿผโ€โš–๏ธ|๐Ÿ‘ฉโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš–๏ธ|๐Ÿง‘โ€๐ŸŒพ|๐Ÿง‘๐Ÿพโ€๐ŸŒพ|๐Ÿง‘๐Ÿผโ€๐ŸŒพ|๐Ÿ‘จโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘ฉโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŒพ|๐Ÿง‘โ€๐Ÿณ|๐Ÿง‘๐Ÿพโ€๐Ÿณ|๐Ÿง‘๐Ÿผโ€๐Ÿณ|๐Ÿ‘จโ€๐Ÿณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿณ|๐Ÿ‘ฉโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿณ|๐Ÿง‘โ€๐Ÿ”ง|๐Ÿง‘๐Ÿพโ€๐Ÿ”ง|๐Ÿง‘๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘จโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘ฉโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ง|๐Ÿง‘โ€๐Ÿญ|๐Ÿง‘๐Ÿพโ€๐Ÿญ|๐Ÿง‘๐Ÿผโ€๐Ÿญ|๐Ÿ‘จโ€๐Ÿญ|๐Ÿ‘จ๐Ÿพโ€๐Ÿญ|๐Ÿ‘จ๐Ÿผโ€๐Ÿญ|๐Ÿ‘ฉโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿญ|๐Ÿง‘โ€๐Ÿ’ผ|๐Ÿง‘๐Ÿพโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘จโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘ฉโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ผ|๐Ÿง‘โ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿพโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘จโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘ฉโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿง‘โ€๐Ÿ’ป|๐Ÿง‘๐Ÿพโ€๐Ÿ’ป|๐Ÿง‘๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘จโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘ฉโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป|๐Ÿง‘โ€๐ŸŽค|๐Ÿง‘๐Ÿพโ€๐ŸŽค|๐Ÿง‘๐Ÿผโ€๐ŸŽค|๐Ÿ‘จโ€๐ŸŽค|๐Ÿ‘จ๐Ÿพโ€๐ŸŽค|๐Ÿ‘จ๐Ÿผโ€๐ŸŽค|๐Ÿ‘ฉโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽค|๐Ÿง‘โ€๐ŸŽจ|๐Ÿง‘๐Ÿพโ€๐ŸŽจ|๐Ÿง‘๐Ÿผโ€๐ŸŽจ|๐Ÿ‘จโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘ฉโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽจ|๐Ÿง‘โ€โœˆ๏ธ|๐Ÿง‘๐Ÿพโ€โœˆ๏ธ|๐Ÿง‘๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘จโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘ฉโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โœˆ๏ธ|๐Ÿง‘โ€๐Ÿš€|๐Ÿง‘๐Ÿพโ€๐Ÿš€|๐Ÿง‘๐Ÿผโ€๐Ÿš€|๐Ÿ‘จโ€๐Ÿš€|๐Ÿ‘จ๐Ÿพโ€๐Ÿš€|๐Ÿ‘จ๐Ÿผโ€๐Ÿš€|๐Ÿ‘ฉโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš€|๐Ÿง‘โ€๐Ÿš’|๐Ÿง‘๐Ÿพโ€๐Ÿš’|๐Ÿง‘๐Ÿผโ€๐Ÿš’|๐Ÿ‘จโ€๐Ÿš’|๐Ÿ‘จ๐Ÿพโ€๐Ÿš’|๐Ÿ‘จ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฉโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฎโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฎโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™€๏ธ|๐Ÿ•ต๏ธโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿพโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿผโ€โ™‚๏ธ|๐Ÿ•ต๏ธโ€โ™€๏ธ|๐Ÿ•ต๐Ÿพโ€โ™€๏ธ|๐Ÿ•ต๐Ÿผโ€โ™€๏ธ|๐Ÿ’‚โ€โ™‚๏ธ|๐Ÿ’‚๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‚โ€โ™€๏ธ|๐Ÿ’‚๐Ÿพโ€โ™€๏ธ|๐Ÿ’‚๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ทโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ทโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ณโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ณโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™€๏ธ|๐Ÿคตโ€โ™‚๏ธ|๐Ÿคต๐Ÿพโ€โ™‚๏ธ|๐Ÿคต๐Ÿผโ€โ™‚๏ธ|๐Ÿคตโ€โ™€๏ธ|๐Ÿคต๐Ÿพโ€โ™€๏ธ|๐Ÿคต๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฐโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฐโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฉโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿผ|๐Ÿ‘จโ€๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿผ|๐Ÿง‘โ€๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿผ|๐Ÿง‘โ€๐ŸŽ„|๐Ÿง‘๐Ÿพโ€๐ŸŽ„|๐Ÿง‘๐Ÿผโ€๐ŸŽ„|๐Ÿฆธโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿพโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿผโ€โ™‚๏ธ|๐Ÿฆธโ€โ™€๏ธ|๐Ÿฆธ๐Ÿพโ€โ™€๏ธ|๐Ÿฆธ๐Ÿผโ€โ™€๏ธ|๐Ÿฆนโ€โ™‚๏ธ|๐Ÿฆน๐Ÿพโ€โ™‚๏ธ|๐Ÿฆน๐Ÿผโ€โ™‚๏ธ|๐Ÿฆนโ€โ™€๏ธ|๐Ÿฆน๐Ÿพโ€โ™€๏ธ|๐Ÿฆน๐Ÿผโ€โ™€๏ธ|๐Ÿง™โ€โ™‚๏ธ|๐Ÿง™๐Ÿพโ€โ™‚๏ธ|๐Ÿง™๐Ÿผโ€โ™‚๏ธ|๐Ÿง™โ€โ™€๏ธ|๐Ÿง™๐Ÿพโ€โ™€๏ธ|๐Ÿง™๐Ÿผโ€โ™€๏ธ|๐Ÿงšโ€โ™‚๏ธ|๐Ÿงš๐Ÿพโ€โ™‚๏ธ|๐Ÿงš๐Ÿผโ€โ™‚๏ธ|๐Ÿงšโ€โ™€๏ธ|๐Ÿงš๐Ÿพโ€โ™€๏ธ|๐Ÿงš๐Ÿผโ€โ™€๏ธ|๐Ÿง›โ€โ™‚๏ธ|๐Ÿง›๐Ÿพโ€โ™‚๏ธ|๐Ÿง›๐Ÿผโ€โ™‚๏ธ|๐Ÿง›โ€โ™€๏ธ|๐Ÿง›๐Ÿพโ€โ™€๏ธ|๐Ÿง›๐Ÿผโ€โ™€๏ธ|๐Ÿงœโ€โ™‚๏ธ|๐Ÿงœ๐Ÿพโ€โ™‚๏ธ|๐Ÿงœ๐Ÿผโ€โ™‚๏ธ|๐Ÿงœโ€โ™€๏ธ|๐Ÿงœ๐Ÿพโ€โ™€๏ธ|๐Ÿงœ๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿงžโ€โ™‚๏ธ|๐ŸงŸโ€โ™‚๏ธ|๐Ÿ’†โ€โ™‚๏ธ|๐Ÿ’†๐Ÿพโ€โ™‚๏ธ|๐Ÿ’†๐Ÿผโ€โ™‚๏ธ|๐Ÿ’†โ€โ™€๏ธ|๐Ÿ’†๐Ÿพโ€โ™€๏ธ|๐Ÿ’†๐Ÿผโ€โ™€๏ธ|๐Ÿ’‡โ€โ™‚๏ธ|๐Ÿ’‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‡โ€โ™€๏ธ|๐Ÿ’‡๐Ÿพโ€โ™€๏ธ|๐Ÿ’‡๐Ÿผโ€โ™€๏ธ|๐Ÿšถโ€โ™‚๏ธ|๐Ÿšถ๐Ÿพโ€โ™‚๏ธ|๐Ÿšถ๐Ÿผโ€โ™‚๏ธ|๐Ÿšถโ€โ™€๏ธ|๐Ÿšถ๐Ÿพโ€โ™€๏ธ|๐Ÿšถ๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐ŸงŽโ€โ™‚๏ธ|๐ŸงŽ๐Ÿพโ€โ™‚๏ธ|๐ŸงŽ๐Ÿผโ€โ™‚๏ธ|๐ŸงŽโ€โ™€๏ธ|๐ŸงŽ๐Ÿพโ€โ™€๏ธ|๐ŸงŽ๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€๐Ÿฆฏ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘จโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘ฉโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฏ|๐Ÿง‘โ€๐Ÿฆผ|๐Ÿง‘๐Ÿพโ€๐Ÿฆผ|๐Ÿง‘๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘จโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘ฉโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆผ|๐Ÿง‘โ€๐Ÿฆฝ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘จโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘ฉโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฝ|๐Ÿƒโ€โ™‚๏ธ|๐Ÿƒ๐Ÿพโ€โ™‚๏ธ|๐Ÿƒ๐Ÿผโ€โ™‚๏ธ|๐Ÿƒโ€โ™€๏ธ|๐Ÿƒ๐Ÿพโ€โ™€๏ธ|๐Ÿƒ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฏโ€โ™‚๏ธ|๐Ÿง–โ€โ™‚๏ธ|๐Ÿง–๐Ÿพโ€โ™‚๏ธ|๐Ÿง–๐Ÿผโ€โ™‚๏ธ|๐Ÿง–โ€โ™€๏ธ|๐Ÿง–๐Ÿพโ€โ™€๏ธ|๐Ÿง–๐Ÿผโ€โ™€๏ธ|๐Ÿง—โ€โ™‚๏ธ|๐Ÿง—๐Ÿพโ€โ™‚๏ธ|๐Ÿง—๐Ÿผโ€โ™‚๏ธ|๐Ÿง—โ€โ™€๏ธ|๐Ÿง—๐Ÿพโ€โ™€๏ธ|๐Ÿง—๐Ÿผโ€โ™€๏ธ|๐ŸŒ๏ธโ€โ™‚๏ธ|๐ŸŒ๐Ÿพโ€โ™‚๏ธ|๐ŸŒ๐Ÿผโ€โ™‚๏ธ|๐ŸŒ๏ธโ€โ™€๏ธ|๐ŸŒ๐Ÿพโ€โ™€๏ธ|๐ŸŒ๐Ÿผโ€โ™€๏ธ|๐Ÿ„โ€โ™‚๏ธ|๐Ÿ„๐Ÿพโ€โ™‚๏ธ|๐Ÿ„๐Ÿผโ€โ™‚๏ธ|๐Ÿ„โ€โ™€๏ธ|๐Ÿ„๐Ÿพโ€โ™€๏ธ|๐Ÿ„๐Ÿผโ€โ™€๏ธ|๐Ÿšฃโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿพโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿผโ€โ™‚๏ธ|๐Ÿšฃโ€โ™€๏ธ|๐Ÿšฃ๐Ÿพโ€โ™€๏ธ|๐Ÿšฃ๐Ÿผโ€โ™€๏ธ|๐ŸŠโ€โ™‚๏ธ|๐ŸŠ๐Ÿพโ€โ™‚๏ธ|๐ŸŠ๐Ÿผโ€โ™‚๏ธ|๐ŸŠโ€โ™€๏ธ|๐ŸŠ๐Ÿพโ€โ™€๏ธ|๐ŸŠ๐Ÿผโ€โ™€๏ธ|โ›น๏ธโ€โ™‚๏ธ|โ›น๐Ÿพโ€โ™‚๏ธ|โ›น๐Ÿผโ€โ™‚๏ธ|โ›น๏ธโ€โ™€๏ธ|โ›น๐Ÿพโ€โ™€๏ธ|โ›น๐Ÿผโ€โ™€๏ธ|๐Ÿ‹๏ธโ€โ™‚๏ธ|๐Ÿ‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ‹๏ธโ€โ™€๏ธ|๐Ÿ‹๐Ÿพโ€โ™€๏ธ|๐Ÿ‹๐Ÿผโ€โ™€๏ธ|๐Ÿšดโ€โ™‚๏ธ|๐Ÿšด๐Ÿพโ€โ™‚๏ธ|๐Ÿšด๐Ÿผโ€โ™‚๏ธ|๐Ÿšดโ€โ™€๏ธ|๐Ÿšด๐Ÿพโ€โ™€๏ธ|๐Ÿšด๐Ÿผโ€โ™€๏ธ|๐Ÿšตโ€โ™‚๏ธ|๐Ÿšต๐Ÿพโ€โ™‚๏ธ|๐Ÿšต๐Ÿผโ€โ™‚๏ธ|๐Ÿšตโ€โ™€๏ธ|๐Ÿšต๐Ÿพโ€โ™€๏ธ|๐Ÿšต๐Ÿผโ€โ™€๏ธ|๐Ÿคธโ€โ™‚๏ธ|๐Ÿคธ๐Ÿพโ€โ™‚๏ธ|๐Ÿคธ๐Ÿผโ€โ™‚๏ธ|๐Ÿคธโ€โ™€๏ธ|๐Ÿคธ๐Ÿพโ€โ™€๏ธ|๐Ÿคธ๐Ÿผโ€โ™€๏ธ|๐Ÿคผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™€๏ธ|๐Ÿคฝ๐Ÿพโ€โ™€๏ธ|๐Ÿคฝ๐Ÿผโ€โ™€๏ธ|๐Ÿคพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿผโ€โ™‚๏ธ|๐Ÿคพโ€โ™€๏ธ|๐Ÿคพ๐Ÿพโ€โ™€๏ธ|๐Ÿคพ๐Ÿผโ€โ™€๏ธ|๐Ÿคนโ€โ™‚๏ธ|๐Ÿคน๐Ÿพโ€โ™‚๏ธ|๐Ÿคน๐Ÿผโ€โ™‚๏ธ|๐Ÿคนโ€โ™€๏ธ|๐Ÿคน๐Ÿพโ€โ™€๏ธ|๐Ÿคน๐Ÿผโ€โ™€๏ธ|๐Ÿง˜โ€โ™‚๏ธ|๐Ÿง˜๐Ÿพโ€โ™‚๏ธ|๐Ÿง˜๐Ÿผโ€โ™‚๏ธ|๐Ÿง˜โ€โ™€๏ธ|๐Ÿง˜๐Ÿพโ€โ™€๏ธ|๐Ÿง˜๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ•โ€๐Ÿฆบ|๐Ÿˆโ€โฌ›|๐Ÿปโ€โ„๏ธ|๐Ÿณ๏ธโ€๐ŸŒˆ|๐Ÿดโ€โ˜ ๏ธ)/gu, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, From 8b10df286b48785baa1aad005ab2b47bcd1c4f82 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Wed, 26 Jul 2023 11:43:30 +0530 Subject: [PATCH 009/214] fix: lint issue --- src/libs/EmojiUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 125e391f12d4..b2eae17ec8d1 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -276,6 +276,7 @@ function extractEmojis(text) { if (sequenceEmojis) { sequenceEmojis = [...new Set(sequenceEmojis)]; + // eslint-disable-next-line no-restricted-syntax for (const sequenceEmoji of sequenceEmojis) { const regex = new RegExp(sequenceEmoji, 'gu'); str = str.replace(regex, ''); From af59f1b0535c7a94afde6225d8e15c26592f2945 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Thu, 27 Jul 2023 22:28:23 +0530 Subject: [PATCH 010/214] fix: emoji not adding due to null sequence --- src/libs/EmojiUtils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 064369c54ac4..a54bf00fe8f3 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -282,6 +282,8 @@ function extractEmojis(text) { const regex = new RegExp(sequenceEmoji, 'gu'); str = str.replace(regex, ''); } + } else { + sequenceEmojis = []; } let parseEmojis = str.match(CONST.REGEX.EMOJIS); From a8b6aaa62487286951eedfe90338578bb950a8f2 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Sat, 29 Jul 2023 04:06:24 +0530 Subject: [PATCH 011/214] fix: used all the emoji constants Ref Link : https://unicode.org/reports/tr18/#RL2.7 --- src/CONST.js | 6 ++++-- src/libs/EmojiUtils.js | 21 ++++----------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 6aebc38a165e..0b450d93cbb3 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1151,8 +1151,10 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, - EMOJI_ZWJ_SEQUENCES: - /(๐Ÿ˜ถโ€๐ŸŒซ๏ธ|๐Ÿ˜ฎโ€๐Ÿ’จ|๐Ÿ˜ตโ€๐Ÿ’ซ|โค๏ธโ€๐Ÿ”ฅ|๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ|๐Ÿง”โ€โ™‚๏ธ|๐Ÿง”๐Ÿพโ€โ™‚๏ธ|๐Ÿง”๐Ÿผโ€โ™‚๏ธ|๐Ÿง”โ€โ™€๏ธ|๐Ÿง”๐Ÿพโ€โ™€๏ธ|๐Ÿง”๐Ÿผโ€โ™€๏ธ|๐Ÿ‘จโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘จโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘จโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘จโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฉโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฐ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฐ|๐Ÿง‘โ€๐Ÿฆฐ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฐ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฐ|๐Ÿ‘ฉโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฑ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฑ|๐Ÿง‘โ€๐Ÿฆฑ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฑ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฑ|๐Ÿ‘ฉโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆณ|๐Ÿง‘โ€๐Ÿฆณ|๐Ÿง‘๐Ÿพโ€๐Ÿฆณ|๐Ÿง‘๐Ÿผโ€๐Ÿฆณ|๐Ÿ‘ฉโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฒ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฒ|๐Ÿง‘โ€๐Ÿฆฒ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฒ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฒ|๐Ÿ‘ฑโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฑโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฑ๐Ÿผโ€โ™‚๏ธ|๐Ÿ™โ€โ™‚๏ธ|๐Ÿ™๐Ÿพโ€โ™‚๏ธ|๐Ÿ™๐Ÿผโ€โ™‚๏ธ|๐Ÿ™โ€โ™€๏ธ|๐Ÿ™๐Ÿพโ€โ™€๏ธ|๐Ÿ™๐Ÿผโ€โ™€๏ธ|๐Ÿ™Žโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™‚๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™‚๏ธ|๐Ÿ™Žโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿพโ€โ™€๏ธ|๐Ÿ™Ž๐Ÿผโ€โ™€๏ธ|๐Ÿ™…โ€โ™‚๏ธ|๐Ÿ™…๐Ÿพโ€โ™‚๏ธ|๐Ÿ™…๐Ÿผโ€โ™‚๏ธ|๐Ÿ™…โ€โ™€๏ธ|๐Ÿ™…๐Ÿพโ€โ™€๏ธ|๐Ÿ™…๐Ÿผโ€โ™€๏ธ|๐Ÿ™†โ€โ™‚๏ธ|๐Ÿ™†๐Ÿพโ€โ™‚๏ธ|๐Ÿ™†๐Ÿผโ€โ™‚๏ธ|๐Ÿ™†โ€โ™€๏ธ|๐Ÿ™†๐Ÿพโ€โ™€๏ธ|๐Ÿ™†๐Ÿผโ€โ™€๏ธ|๐Ÿ’โ€โ™‚๏ธ|๐Ÿ’๐Ÿพโ€โ™‚๏ธ|๐Ÿ’๐Ÿผโ€โ™‚๏ธ|๐Ÿ’โ€โ™€๏ธ|๐Ÿ’๐Ÿพโ€โ™€๏ธ|๐Ÿ’๐Ÿผโ€โ™€๏ธ|๐Ÿ™‹โ€โ™‚๏ธ|๐Ÿ™‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‹โ€โ™€๏ธ|๐Ÿ™‹๐Ÿพโ€โ™€๏ธ|๐Ÿ™‹๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿ™‡โ€โ™‚๏ธ|๐Ÿ™‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ™‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ™‡โ€โ™€๏ธ|๐Ÿ™‡๐Ÿพโ€โ™€๏ธ|๐Ÿ™‡๐Ÿผโ€โ™€๏ธ|๐Ÿคฆโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฆโ€โ™€๏ธ|๐Ÿคฆ๐Ÿพโ€โ™€๏ธ|๐Ÿคฆ๐Ÿผโ€โ™€๏ธ|๐Ÿคทโ€โ™‚๏ธ|๐Ÿคท๐Ÿพโ€โ™‚๏ธ|๐Ÿคท๐Ÿผโ€โ™‚๏ธ|๐Ÿคทโ€โ™€๏ธ|๐Ÿคท๐Ÿพโ€โ™€๏ธ|๐Ÿคท๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€โš•๏ธ|๐Ÿง‘๐Ÿพโ€โš•๏ธ|๐Ÿง‘๐Ÿผโ€โš•๏ธ|๐Ÿ‘จโ€โš•๏ธ|๐Ÿ‘จ๐Ÿพโ€โš•๏ธ|๐Ÿ‘จ๐Ÿผโ€โš•๏ธ|๐Ÿ‘ฉโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš•๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš•๏ธ|๐Ÿง‘โ€๐ŸŽ“|๐Ÿง‘๐Ÿพโ€๐ŸŽ“|๐Ÿง‘๐Ÿผโ€๐ŸŽ“|๐Ÿ‘จโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘จ๐Ÿผโ€๐ŸŽ“|๐Ÿ‘ฉโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽ“|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽ“|๐Ÿง‘โ€๐Ÿซ|๐Ÿง‘๐Ÿพโ€๐Ÿซ|๐Ÿง‘๐Ÿผโ€๐Ÿซ|๐Ÿ‘จโ€๐Ÿซ|๐Ÿ‘จ๐Ÿพโ€๐Ÿซ|๐Ÿ‘จ๐Ÿผโ€๐Ÿซ|๐Ÿ‘ฉโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿซ|๐Ÿง‘โ€โš–๏ธ|๐Ÿง‘๐Ÿพโ€โš–๏ธ|๐Ÿง‘๐Ÿผโ€โš–๏ธ|๐Ÿ‘จโ€โš–๏ธ|๐Ÿ‘จ๐Ÿพโ€โš–๏ธ|๐Ÿ‘จ๐Ÿผโ€โš–๏ธ|๐Ÿ‘ฉโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โš–๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โš–๏ธ|๐Ÿง‘โ€๐ŸŒพ|๐Ÿง‘๐Ÿพโ€๐ŸŒพ|๐Ÿง‘๐Ÿผโ€๐ŸŒพ|๐Ÿ‘จโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘จ๐Ÿผโ€๐ŸŒพ|๐Ÿ‘ฉโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŒพ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŒพ|๐Ÿง‘โ€๐Ÿณ|๐Ÿง‘๐Ÿพโ€๐Ÿณ|๐Ÿง‘๐Ÿผโ€๐Ÿณ|๐Ÿ‘จโ€๐Ÿณ|๐Ÿ‘จ๐Ÿพโ€๐Ÿณ|๐Ÿ‘จ๐Ÿผโ€๐Ÿณ|๐Ÿ‘ฉโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿณ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿณ|๐Ÿง‘โ€๐Ÿ”ง|๐Ÿง‘๐Ÿพโ€๐Ÿ”ง|๐Ÿง‘๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘จโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ง|๐Ÿ‘ฉโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ง|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ง|๐Ÿง‘โ€๐Ÿญ|๐Ÿง‘๐Ÿพโ€๐Ÿญ|๐Ÿง‘๐Ÿผโ€๐Ÿญ|๐Ÿ‘จโ€๐Ÿญ|๐Ÿ‘จ๐Ÿพโ€๐Ÿญ|๐Ÿ‘จ๐Ÿผโ€๐Ÿญ|๐Ÿ‘ฉโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿญ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿญ|๐Ÿง‘โ€๐Ÿ’ผ|๐Ÿง‘๐Ÿพโ€๐Ÿ’ผ|๐Ÿง‘๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘จโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ผ|๐Ÿ‘ฉโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ผ|๐Ÿง‘โ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿพโ€๐Ÿ”ฌ|๐Ÿง‘๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘จโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘จ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿ‘ฉโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ”ฌ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ”ฌ|๐Ÿง‘โ€๐Ÿ’ป|๐Ÿง‘๐Ÿพโ€๐Ÿ’ป|๐Ÿง‘๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘จโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป|๐Ÿ‘ฉโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿ’ป|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป|๐Ÿง‘โ€๐ŸŽค|๐Ÿง‘๐Ÿพโ€๐ŸŽค|๐Ÿง‘๐Ÿผโ€๐ŸŽค|๐Ÿ‘จโ€๐ŸŽค|๐Ÿ‘จ๐Ÿพโ€๐ŸŽค|๐Ÿ‘จ๐Ÿผโ€๐ŸŽค|๐Ÿ‘ฉโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽค|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽค|๐Ÿง‘โ€๐ŸŽจ|๐Ÿง‘๐Ÿพโ€๐ŸŽจ|๐Ÿง‘๐Ÿผโ€๐ŸŽจ|๐Ÿ‘จโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘จ๐Ÿผโ€๐ŸŽจ|๐Ÿ‘ฉโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿพโ€๐ŸŽจ|๐Ÿ‘ฉ๐Ÿผโ€๐ŸŽจ|๐Ÿง‘โ€โœˆ๏ธ|๐Ÿง‘๐Ÿพโ€โœˆ๏ธ|๐Ÿง‘๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘จโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘จ๐Ÿผโ€โœˆ๏ธ|๐Ÿ‘ฉโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿพโ€โœˆ๏ธ|๐Ÿ‘ฉ๐Ÿผโ€โœˆ๏ธ|๐Ÿง‘โ€๐Ÿš€|๐Ÿง‘๐Ÿพโ€๐Ÿš€|๐Ÿง‘๐Ÿผโ€๐Ÿš€|๐Ÿ‘จโ€๐Ÿš€|๐Ÿ‘จ๐Ÿพโ€๐Ÿš€|๐Ÿ‘จ๐Ÿผโ€๐Ÿš€|๐Ÿ‘ฉโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš€|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš€|๐Ÿง‘โ€๐Ÿš’|๐Ÿง‘๐Ÿพโ€๐Ÿš’|๐Ÿง‘๐Ÿผโ€๐Ÿš’|๐Ÿ‘จโ€๐Ÿš’|๐Ÿ‘จ๐Ÿพโ€๐Ÿš’|๐Ÿ‘จ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฉโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿš’|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿš’|๐Ÿ‘ฎโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฎโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฎ๐Ÿผโ€โ™€๏ธ|๐Ÿ•ต๏ธโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿพโ€โ™‚๏ธ|๐Ÿ•ต๐Ÿผโ€โ™‚๏ธ|๐Ÿ•ต๏ธโ€โ™€๏ธ|๐Ÿ•ต๐Ÿพโ€โ™€๏ธ|๐Ÿ•ต๐Ÿผโ€โ™€๏ธ|๐Ÿ’‚โ€โ™‚๏ธ|๐Ÿ’‚๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‚๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‚โ€โ™€๏ธ|๐Ÿ’‚๐Ÿพโ€โ™€๏ธ|๐Ÿ’‚๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ทโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ทโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ท๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ณโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ณโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ณ๐Ÿผโ€โ™€๏ธ|๐Ÿคตโ€โ™‚๏ธ|๐Ÿคต๐Ÿพโ€โ™‚๏ธ|๐Ÿคต๐Ÿผโ€โ™‚๏ธ|๐Ÿคตโ€โ™€๏ธ|๐Ÿคต๐Ÿพโ€โ™€๏ธ|๐Ÿคต๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฐโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™‚๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™‚๏ธ|๐Ÿ‘ฐโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿพโ€โ™€๏ธ|๐Ÿ‘ฐ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฉโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿผ|๐Ÿ‘จโ€๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿผ|๐Ÿง‘โ€๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿผ|๐Ÿง‘โ€๐ŸŽ„|๐Ÿง‘๐Ÿพโ€๐ŸŽ„|๐Ÿง‘๐Ÿผโ€๐ŸŽ„|๐Ÿฆธโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿพโ€โ™‚๏ธ|๐Ÿฆธ๐Ÿผโ€โ™‚๏ธ|๐Ÿฆธโ€โ™€๏ธ|๐Ÿฆธ๐Ÿพโ€โ™€๏ธ|๐Ÿฆธ๐Ÿผโ€โ™€๏ธ|๐Ÿฆนโ€โ™‚๏ธ|๐Ÿฆน๐Ÿพโ€โ™‚๏ธ|๐Ÿฆน๐Ÿผโ€โ™‚๏ธ|๐Ÿฆนโ€โ™€๏ธ|๐Ÿฆน๐Ÿพโ€โ™€๏ธ|๐Ÿฆน๐Ÿผโ€โ™€๏ธ|๐Ÿง™โ€โ™‚๏ธ|๐Ÿง™๐Ÿพโ€โ™‚๏ธ|๐Ÿง™๐Ÿผโ€โ™‚๏ธ|๐Ÿง™โ€โ™€๏ธ|๐Ÿง™๐Ÿพโ€โ™€๏ธ|๐Ÿง™๐Ÿผโ€โ™€๏ธ|๐Ÿงšโ€โ™‚๏ธ|๐Ÿงš๐Ÿพโ€โ™‚๏ธ|๐Ÿงš๐Ÿผโ€โ™‚๏ธ|๐Ÿงšโ€โ™€๏ธ|๐Ÿงš๐Ÿพโ€โ™€๏ธ|๐Ÿงš๐Ÿผโ€โ™€๏ธ|๐Ÿง›โ€โ™‚๏ธ|๐Ÿง›๐Ÿพโ€โ™‚๏ธ|๐Ÿง›๐Ÿผโ€โ™‚๏ธ|๐Ÿง›โ€โ™€๏ธ|๐Ÿง›๐Ÿพโ€โ™€๏ธ|๐Ÿง›๐Ÿผโ€โ™€๏ธ|๐Ÿงœโ€โ™‚๏ธ|๐Ÿงœ๐Ÿพโ€โ™‚๏ธ|๐Ÿงœ๐Ÿผโ€โ™‚๏ธ|๐Ÿงœโ€โ™€๏ธ|๐Ÿงœ๐Ÿพโ€โ™€๏ธ|๐Ÿงœ๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐Ÿงžโ€โ™‚๏ธ|๐ŸงŸโ€โ™‚๏ธ|๐Ÿ’†โ€โ™‚๏ธ|๐Ÿ’†๐Ÿพโ€โ™‚๏ธ|๐Ÿ’†๐Ÿผโ€โ™‚๏ธ|๐Ÿ’†โ€โ™€๏ธ|๐Ÿ’†๐Ÿพโ€โ™€๏ธ|๐Ÿ’†๐Ÿผโ€โ™€๏ธ|๐Ÿ’‡โ€โ™‚๏ธ|๐Ÿ’‡๐Ÿพโ€โ™‚๏ธ|๐Ÿ’‡๐Ÿผโ€โ™‚๏ธ|๐Ÿ’‡โ€โ™€๏ธ|๐Ÿ’‡๐Ÿพโ€โ™€๏ธ|๐Ÿ’‡๐Ÿผโ€โ™€๏ธ|๐Ÿšถโ€โ™‚๏ธ|๐Ÿšถ๐Ÿพโ€โ™‚๏ธ|๐Ÿšถ๐Ÿผโ€โ™‚๏ธ|๐Ÿšถโ€โ™€๏ธ|๐Ÿšถ๐Ÿพโ€โ™€๏ธ|๐Ÿšถ๐Ÿผโ€โ™€๏ธ|๐Ÿงโ€โ™‚๏ธ|๐Ÿง๐Ÿพโ€โ™‚๏ธ|๐Ÿง๐Ÿผโ€โ™‚๏ธ|๐Ÿงโ€โ™€๏ธ|๐Ÿง๐Ÿพโ€โ™€๏ธ|๐Ÿง๐Ÿผโ€โ™€๏ธ|๐ŸงŽโ€โ™‚๏ธ|๐ŸงŽ๐Ÿพโ€โ™‚๏ธ|๐ŸงŽ๐Ÿผโ€โ™‚๏ธ|๐ŸงŽโ€โ™€๏ธ|๐ŸงŽ๐Ÿพโ€โ™€๏ธ|๐ŸงŽ๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€๐Ÿฆฏ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฏ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘จโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฏ|๐Ÿ‘ฉโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฏ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฏ|๐Ÿง‘โ€๐Ÿฆผ|๐Ÿง‘๐Ÿพโ€๐Ÿฆผ|๐Ÿง‘๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘จโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆผ|๐Ÿ‘ฉโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆผ|๐Ÿง‘โ€๐Ÿฆฝ|๐Ÿง‘๐Ÿพโ€๐Ÿฆฝ|๐Ÿง‘๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘จโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘จ๐Ÿผโ€๐Ÿฆฝ|๐Ÿ‘ฉโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿฆฝ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿฆฝ|๐Ÿƒโ€โ™‚๏ธ|๐Ÿƒ๐Ÿพโ€โ™‚๏ธ|๐Ÿƒ๐Ÿผโ€โ™‚๏ธ|๐Ÿƒโ€โ™€๏ธ|๐Ÿƒ๐Ÿพโ€โ™€๏ธ|๐Ÿƒ๐Ÿผโ€โ™€๏ธ|๐Ÿ‘ฏโ€โ™‚๏ธ|๐Ÿง–โ€โ™‚๏ธ|๐Ÿง–๐Ÿพโ€โ™‚๏ธ|๐Ÿง–๐Ÿผโ€โ™‚๏ธ|๐Ÿง–โ€โ™€๏ธ|๐Ÿง–๐Ÿพโ€โ™€๏ธ|๐Ÿง–๐Ÿผโ€โ™€๏ธ|๐Ÿง—โ€โ™‚๏ธ|๐Ÿง—๐Ÿพโ€โ™‚๏ธ|๐Ÿง—๐Ÿผโ€โ™‚๏ธ|๐Ÿง—โ€โ™€๏ธ|๐Ÿง—๐Ÿพโ€โ™€๏ธ|๐Ÿง—๐Ÿผโ€โ™€๏ธ|๐ŸŒ๏ธโ€โ™‚๏ธ|๐ŸŒ๐Ÿพโ€โ™‚๏ธ|๐ŸŒ๐Ÿผโ€โ™‚๏ธ|๐ŸŒ๏ธโ€โ™€๏ธ|๐ŸŒ๐Ÿพโ€โ™€๏ธ|๐ŸŒ๐Ÿผโ€โ™€๏ธ|๐Ÿ„โ€โ™‚๏ธ|๐Ÿ„๐Ÿพโ€โ™‚๏ธ|๐Ÿ„๐Ÿผโ€โ™‚๏ธ|๐Ÿ„โ€โ™€๏ธ|๐Ÿ„๐Ÿพโ€โ™€๏ธ|๐Ÿ„๐Ÿผโ€โ™€๏ธ|๐Ÿšฃโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿพโ€โ™‚๏ธ|๐Ÿšฃ๐Ÿผโ€โ™‚๏ธ|๐Ÿšฃโ€โ™€๏ธ|๐Ÿšฃ๐Ÿพโ€โ™€๏ธ|๐Ÿšฃ๐Ÿผโ€โ™€๏ธ|๐ŸŠโ€โ™‚๏ธ|๐ŸŠ๐Ÿพโ€โ™‚๏ธ|๐ŸŠ๐Ÿผโ€โ™‚๏ธ|๐ŸŠโ€โ™€๏ธ|๐ŸŠ๐Ÿพโ€โ™€๏ธ|๐ŸŠ๐Ÿผโ€โ™€๏ธ|โ›น๏ธโ€โ™‚๏ธ|โ›น๐Ÿพโ€โ™‚๏ธ|โ›น๐Ÿผโ€โ™‚๏ธ|โ›น๏ธโ€โ™€๏ธ|โ›น๐Ÿพโ€โ™€๏ธ|โ›น๐Ÿผโ€โ™€๏ธ|๐Ÿ‹๏ธโ€โ™‚๏ธ|๐Ÿ‹๐Ÿพโ€โ™‚๏ธ|๐Ÿ‹๐Ÿผโ€โ™‚๏ธ|๐Ÿ‹๏ธโ€โ™€๏ธ|๐Ÿ‹๐Ÿพโ€โ™€๏ธ|๐Ÿ‹๐Ÿผโ€โ™€๏ธ|๐Ÿšดโ€โ™‚๏ธ|๐Ÿšด๐Ÿพโ€โ™‚๏ธ|๐Ÿšด๐Ÿผโ€โ™‚๏ธ|๐Ÿšดโ€โ™€๏ธ|๐Ÿšด๐Ÿพโ€โ™€๏ธ|๐Ÿšด๐Ÿผโ€โ™€๏ธ|๐Ÿšตโ€โ™‚๏ธ|๐Ÿšต๐Ÿพโ€โ™‚๏ธ|๐Ÿšต๐Ÿผโ€โ™‚๏ธ|๐Ÿšตโ€โ™€๏ธ|๐Ÿšต๐Ÿพโ€โ™€๏ธ|๐Ÿšต๐Ÿผโ€โ™€๏ธ|๐Ÿคธโ€โ™‚๏ธ|๐Ÿคธ๐Ÿพโ€โ™‚๏ธ|๐Ÿคธ๐Ÿผโ€โ™‚๏ธ|๐Ÿคธโ€โ™€๏ธ|๐Ÿคธ๐Ÿพโ€โ™€๏ธ|๐Ÿคธ๐Ÿผโ€โ™€๏ธ|๐Ÿคผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿพโ€โ™‚๏ธ|๐Ÿคฝ๐Ÿผโ€โ™‚๏ธ|๐Ÿคฝโ€โ™€๏ธ|๐Ÿคฝ๐Ÿพโ€โ™€๏ธ|๐Ÿคฝ๐Ÿผโ€โ™€๏ธ|๐Ÿคพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿพโ€โ™‚๏ธ|๐Ÿคพ๐Ÿผโ€โ™‚๏ธ|๐Ÿคพโ€โ™€๏ธ|๐Ÿคพ๐Ÿพโ€โ™€๏ธ|๐Ÿคพ๐Ÿผโ€โ™€๏ธ|๐Ÿคนโ€โ™‚๏ธ|๐Ÿคน๐Ÿพโ€โ™‚๏ธ|๐Ÿคน๐Ÿผโ€โ™‚๏ธ|๐Ÿคนโ€โ™€๏ธ|๐Ÿคน๐Ÿพโ€โ™€๏ธ|๐Ÿคน๐Ÿผโ€โ™€๏ธ|๐Ÿง˜โ€โ™‚๏ธ|๐Ÿง˜๐Ÿพโ€โ™‚๏ธ|๐Ÿง˜๐Ÿผโ€โ™‚๏ธ|๐Ÿง˜โ€โ™€๏ธ|๐Ÿง˜๐Ÿพโ€โ™€๏ธ|๐Ÿง˜๐Ÿผโ€โ™€๏ธ|๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฝโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿผโ€๐Ÿคโ€๐Ÿง‘๐Ÿป|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿปโ€๐Ÿคโ€๐Ÿง‘๐Ÿผ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿฝโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿปโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿง‘๐Ÿฝ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿพ|๐Ÿง‘๐Ÿฟโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿพโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿฝโ€โค๏ธโ€๐Ÿง‘๐Ÿผ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿผโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฟ|๐Ÿง‘๐Ÿปโ€โค๏ธโ€๐Ÿง‘๐Ÿฝ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿพโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฟ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿฝ|๐Ÿ‘จ๐Ÿผโ€โค๏ธโ€๐Ÿ‘จ๐Ÿป|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿพ|๐Ÿ‘จ๐Ÿปโ€โค๏ธโ€๐Ÿ‘จ๐Ÿผ|๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฟโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿพโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿฝโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฟ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿฝ|๐Ÿ‘ฉ๐Ÿผโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿป|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿพ|๐Ÿ‘ฉ๐Ÿปโ€โค๏ธโ€๐Ÿ‘ฉ๐Ÿผ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ|๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ฆ|๐Ÿ‘ฉโ€๐Ÿ‘ง|๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง|๐Ÿ•โ€๐Ÿฆบ|๐Ÿˆโ€โฌ›|๐Ÿปโ€โ„๏ธ|๐Ÿณ๏ธโ€๐ŸŒˆ|๐Ÿดโ€โ˜ ๏ธ)/gu, + + EMOJIS_AND_ZWJ_SEQUENCE: + /[\p{Emoji}\p{Emoji_Presentation}\p{Emoji_Modifier}\p{Emoji_Modifier_Base}\p{Emoji_Component}\p{Extended_Pictographic}\p{Basic_Emoji}\p{Emoji_Keycap_Sequence}\p{RGI_Emoji_Modifier_Sequence}\p{RGI_Emoji_Flag_Sequence}\p{RGI_Emoji_Tag_Sequence}\p{RGI_Emoji_ZWJ_Sequence}\p{RGI_Emoji}]/gu, + TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index a54bf00fe8f3..7e227151d1c6 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -266,33 +266,20 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { - let str = `${text}`; + const str = `${text}`; const emojis = []; if (!str) { return emojis; } - let sequenceEmojis = str.match(CONST.REGEX.EMOJI_ZWJ_SEQUENCES); + let parseEmojis = str.match(CONST.REGEX.EMOJIS_AND_ZWJ_SEQUENCE); - if (sequenceEmojis) { - sequenceEmojis = [...new Set(sequenceEmojis)]; - // eslint-disable-next-line no-restricted-syntax - for (const sequenceEmoji of sequenceEmojis) { - const regex = new RegExp(sequenceEmoji, 'gu'); - str = str.replace(regex, ''); - } - } else { - sequenceEmojis = []; - } - - let parseEmojis = str.match(CONST.REGEX.EMOJIS); - - if (!parseEmojis && !sequenceEmojis) { + if (!parseEmojis) { return emojis; } - parseEmojis = [...new Set(parseEmojis), ...sequenceEmojis]; + parseEmojis = [...new Set(parseEmojis)]; for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; From eb1ec248124767e15209326ab1341b2947a728d3 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Sat, 29 Jul 2023 21:44:09 +0530 Subject: [PATCH 012/214] Update CONST.js --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index 0b450d93cbb3..c9c9a245eae1 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1153,7 +1153,7 @@ const CONST = { EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, EMOJIS_AND_ZWJ_SEQUENCE: - /[\p{Emoji}\p{Emoji_Presentation}\p{Emoji_Modifier}\p{Emoji_Modifier_Base}\p{Emoji_Component}\p{Extended_Pictographic}\p{Basic_Emoji}\p{Emoji_Keycap_Sequence}\p{RGI_Emoji_Modifier_Sequence}\p{RGI_Emoji_Flag_Sequence}\p{RGI_Emoji_Tag_Sequence}\p{RGI_Emoji_ZWJ_Sequence}\p{RGI_Emoji}]/gu, + /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, From d863fef5e3e61202e6c1cf07a48c7a234778ff51 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 31 Jul 2023 09:55:39 +0530 Subject: [PATCH 013/214] chore: remove extra regex for emoji and use updated one --- src/CONST.js | 5 +---- src/libs/EmojiUtils.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index c9c9a245eae1..8d8e384e9647 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1150,10 +1150,7 @@ const CONST = { ROOM_NAME: /^#[a-z0-9ร -รฟ-]{1,80}$/, // eslint-disable-next-line max-len, no-misleading-character-class - EMOJIS: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, - - EMOJIS_AND_ZWJ_SEQUENCE: - /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, + EMOJIS: /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 7e227151d1c6..296ce7fa015f 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -273,7 +273,7 @@ function extractEmojis(text) { return emojis; } - let parseEmojis = str.match(CONST.REGEX.EMOJIS_AND_ZWJ_SEQUENCE); + let parseEmojis = str.match(CONST.REGEX.EMOJIS); if (!parseEmojis) { return emojis; From 5b982125dc25c7086bb3d1c862274636f04a4154 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Tue, 1 Aug 2023 00:53:51 +0530 Subject: [PATCH 014/214] chore: remove extra str and change continue code --- src/libs/EmojiUtils.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 296ce7fa015f..eda27513205e 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -266,14 +266,13 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { - const str = `${text}`; const emojis = []; - if (!str) { + if (!text) { return emojis; } - let parseEmojis = str.match(CONST.REGEX.EMOJIS); + let parseEmojis = text.match(CONST.REGEX.EMOJIS); if (!parseEmojis) { return emojis; @@ -284,8 +283,9 @@ function extractEmojis(text) { for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; const emoji = Emojis.emojiCodeTable[character]; - if (!emoji) continue; // eslint-disable-line no-continue - emojis.push(emoji); + if (emoji) { + emojis.push(emoji); + } } return emojis; From 81845f4a271584e154cdf124d24a077cc099dbfe Mon Sep 17 00:00:00 2001 From: David Cardoza Date: Mon, 31 Jul 2023 19:17:36 -0700 Subject: [PATCH 015/214] Create Card-Rev-Share-for-Approved-Partners.md Added new ExpensifyHelp page. Tracking issue is here - https://github.com/Expensify/Expensify/issues/304373 --- docs/Card-Rev-Share-for-Approved-Partners.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/Card-Rev-Share-for-Approved-Partners.md diff --git a/docs/Card-Rev-Share-for-Approved-Partners.md b/docs/Card-Rev-Share-for-Approved-Partners.md new file mode 100644 index 000000000000..9b5647a004d3 --- /dev/null +++ b/docs/Card-Rev-Share-for-Approved-Partners.md @@ -0,0 +1,17 @@ +--- +title: Expensify Card revenue share for ExpensifyApproved! partners +description: Earn money when your clients adopt the Expensify Card +--- + + +# About +Start making more with us! We're thrilled to announce a new incentive for our US-based ExpensifyApproved! partner accountants. You can now earn additional income for your firm every time your client uses their Expensify Card. We're offering 0.5% of the total Expensify Card spend of your clients in cashback returned to your firm. The more your clients spend, the more cashback your firm receives!
+
This program is currently only available to US-based ExpensifyApproved! partner accountants. + +# How-to +To benefit from this program, all you need to do is ensure that you are listed as a domain admin on your client's Expensify account. If you're not currently a domain admin, your client can follow the instructions outlined in [our help article](https://community.expensify.com/discussion/5749/how-to-add-and-remove-domain-admins#:~:text=Domain%20Admins%20have%20total%20control,a%20member%20of%20the%20domain.) to assign you this role. +# FAQ +- What if my firm is not permitted to accept revenue share from our clients?
+
We understand that different firms may have different policies. If your firm is unable to accept this revenue share, you can pass the revenue share back to your client to give them an additional 0.5% of cash back using your own internal payment tools.

+- What if my firm does not wish to participate in the program?
+
Please reach out to your assigned partner manager at new.expensify.com to inform them you would not like to accept the revenue share nor do you want to pass the revenue share to your clients. From 11717c5772c631af6629d443270b8fbf107bf875 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Tue, 1 Aug 2023 20:41:32 +0800 Subject: [PATCH 016/214] fix keyboard flashing while clicking "Add attachment" --- patches/react-native+0.71.2-alpha.3.patch | 17 +++++++++- src/components/AttachmentPicker/index.js | 13 ++++++-- .../AttachmentPicker/index.native.js | 12 +++++-- src/components/Modal/BaseModal.js | 8 +++++ src/components/Modal/index.android.js | 10 ++++++ src/libs/ComposerFocusManager.js | 23 +++++++++++++ src/pages/home/report/ReportActionCompose.js | 32 ++++++++++++++----- 7 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/libs/ComposerFocusManager.js diff --git a/patches/react-native+0.71.2-alpha.3.patch b/patches/react-native+0.71.2-alpha.3.patch index 822ca9daec9c..e48c71bb2151 100644 --- a/patches/react-native+0.71.2-alpha.3.patch +++ b/patches/react-native+0.71.2-alpha.3.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js -index 2f48f9e..ac7a416 100644 +index 2f48f9e..e26d677 100644 --- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js +++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js @@ -65,6 +65,7 @@ class KeyboardAvoidingView extends React.Component { @@ -86,3 +86,18 @@ index 2f48f9e..ac7a416 100644 componentDidMount(): void { if (Platform.OS === 'ios') { this._subscriptions = [ +diff --git a/node_modules/react-native/React/Views/RCTModalHostViewManager.m b/node_modules/react-native/React/Views/RCTModalHostViewManager.m +index 4b9f9ad..4992874 100644 +--- a/node_modules/react-native/React/Views/RCTModalHostViewManager.m ++++ b/node_modules/react-native/React/Views/RCTModalHostViewManager.m +@@ -79,6 +79,10 @@ RCT_EXPORT_MODULE() + if (self->_presentationBlock) { + self->_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); + } else { ++ UIWindow *window = RCTKeyWindow(); ++ if (window && window.rootViewController && [window.rootViewController.view isFirstResponder]) { ++ [window.rootViewController.view resignFirstResponder]; ++ } + [[modalHostView reactViewController] presentViewController:viewController + animated:animated + completion:completionBlock]; diff --git a/src/components/AttachmentPicker/index.js b/src/components/AttachmentPicker/index.js index e7653df2b4d0..d18165ab57ab 100644 --- a/src/components/AttachmentPicker/index.js +++ b/src/components/AttachmentPicker/index.js @@ -27,6 +27,8 @@ function getAcceptableFileTypes(type) { function AttachmentPicker(props) { const fileInput = useRef(); const onPicked = useRef(); + const onCanceled = useRef(() => {}); + return ( <> e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + if (!fileInput.current) { + return; + } + fileInput.current.addEventListener('cancel', () => onCanceled.current(), { once: true}); + }} accept={getAcceptableFileTypes(props.type)} /> {props.children({ - openPicker: ({onPicked: newOnPicked}) => { + openPicker: ({onPicked: newOnPicked, onCanceled: newOnCanceled = () => {}}) => { onPicked.current = newOnPicked; fileInput.current.click(); + onCanceled.current = newOnCanceled; }, })} diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js index b4b7d0b04c4e..a8f3b8d35c16 100644 --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -126,6 +126,7 @@ class AttachmentPicker extends Component { }); } + this.cancel = () => {}; this.close = this.close.bind(this); this.pickAttachment = this.pickAttachment.bind(this); this.removeKeyboardListener = this.removeKeyboardListener.bind(this); @@ -181,6 +182,7 @@ class AttachmentPicker extends Component { */ pickAttachment(attachments = []) { if (attachments.length === 0) { + this.cancel(); return; } @@ -342,7 +344,10 @@ class AttachmentPicker extends Component { */ renderChildren() { return this.props.children({ - openPicker: ({onPicked}) => this.open(onPicked), + openPicker: ({onPicked, onCanceled = () => {}}) => { + this.open(onPicked); + this.cancel = onCanceled; + }, }); } @@ -350,7 +355,10 @@ class AttachmentPicker extends Component { return ( <> { + this.close(); + this.cancel(); + }} isVisible={this.state.isVisible} anchorPosition={styles.createMenuPosition} onModalHide={this.onModalHide} diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 6d5bd5390416..155e80acab84 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -10,6 +10,7 @@ import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './ import * as Modal from '../../libs/actions/Modal'; import getModalStyles from '../../styles/getModalStyles'; import variables from '../../styles/variables'; +import ComposerFocusManager from '../../libs/ComposerFocusManager'; const propTypes = { ...modalPropTypes, @@ -73,6 +74,9 @@ class BaseModal extends PureComponent { this.props.onModalHide(); } Modal.onModalDidClose(); + if (!this.props.fullscreen) { + ComposerFocusManager.setReadyToFocus(); + } } render() { @@ -109,6 +113,9 @@ class BaseModal extends PureComponent { // Note: Escape key on web/desktop will trigger onBackButtonPress callback // eslint-disable-next-line react/jsx-props-no-multi-spaces onBackButtonPress={this.props.onClose} + onModalWillShow={() => { + ComposerFocusManager.resetReadyToFocus(); + }} onModalShow={() => { if (this.props.shouldSetModalVisibility) { Modal.setModalVisibility(true); @@ -117,6 +124,7 @@ class BaseModal extends PureComponent { }} propagateSwipe={this.props.propagateSwipe} onModalHide={this.hideModal} + onDismiss={() => ComposerFocusManager.setReadyToFocus()} onSwipeComplete={this.props.onClose} swipeDirection={swipeDirection} isVisible={this.props.isVisible} diff --git a/src/components/Modal/index.android.js b/src/components/Modal/index.android.js index 09df74329b20..b5f11a02650a 100644 --- a/src/components/Modal/index.android.js +++ b/src/components/Modal/index.android.js @@ -1,7 +1,17 @@ import React from 'react'; +import {AppState} from 'react-native'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; import {propTypes, defaultProps} from './modalPropTypes'; +import ComposerFocusManager from '../../libs/ComposerFocusManager'; + +AppState.addEventListener('focus', () => { + ComposerFocusManager.setReadyToFocus(); +}); + +AppState.addEventListener('blur', () => { + ComposerFocusManager.resetReadyToFocus(); +}); // Only want to use useNativeDriver on Android. It has strange flashes issue on IOS // https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManager.js new file mode 100644 index 000000000000..1f5f9d2d6f97 --- /dev/null +++ b/src/libs/ComposerFocusManager.js @@ -0,0 +1,23 @@ +let isReadyToFocusPromise = Promise.resolve(); +let resolveIsReadyToFocus; + +function resetReadyToFocus() { + isReadyToFocusPromise = new Promise(resolve => { + resolveIsReadyToFocus = resolve; + }); +} +function setReadyToFocus() { + if (!resolveIsReadyToFocus) { + return; + } + resolveIsReadyToFocus() +} +function isReadyToFocus() { + return isReadyToFocusPromise; +} + +export default { + resetReadyToFocus, + setReadyToFocus, + isReadyToFocus, +}; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 644a90216091..6766cbb0e8b4 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -59,6 +59,7 @@ import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressLis import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; import withAnimatedRef from '../../../components/withAnimatedRef'; import updatePropsPaperWorklet from '../../../libs/updatePropsPaperWorklet'; +import ComposerFocusManager from '../../../libs/ComposerFocusManager'; const propTypes = { /** Beta features list */ @@ -221,6 +222,8 @@ class ReportActionCompose extends React.Component { this.unsubscribeNavigationBlur = () => null; this.unsubscribeNavigationFocus = () => null; + + this.shouldFocusAfterClosingModal = true; this.state = { isFocused: this.shouldFocusInputOnScreenFocus && !this.props.modal.isVisible && !this.props.modal.willAlertModalBecomeVisible && this.props.shouldShowComposeInput, @@ -268,10 +271,13 @@ class ReportActionCompose extends React.Component { } componentDidUpdate(prevProps) { + if (this.props.modal.isVisible && !prevProps.modal.isVisible) { + this.shouldFocusAfterClosingModal = true; + } // We want to focus or refocus the input when a modal has been closed or the underlying screen is refocused. // We avoid doing this on native platforms since the software keyboard popping // open creates a jarring and broken UX. - if (this.willBlurTextInputOnTapOutside && !this.props.modal.isVisible && this.props.isFocused && (prevProps.modal.isVisible || !prevProps.isFocused)) { + if (this.willBlurTextInputOnTapOutside && this.shouldFocusAfterClosingModal && !this.props.modal.isVisible && this.props.isFocused && (prevProps.modal.isVisible || !prevProps.isFocused)) { this.focus(); } @@ -757,13 +763,14 @@ class ReportActionCompose extends React.Component { if (!shouldelay) { this.textInput.focus(); - } else { - // Keyboard is not opened after Emoji Picker is closed - // SetTimeout is used as a workaround - // https://github.com/react-native-modal/react-native-modal/issues/114 - // We carefully choose a delay. 100ms is found enough for keyboard to open. - setTimeout(() => this.textInput.focus(), 100); + return; } + ComposerFocusManager.isReadyToFocus().then(() => { + if (!this.textInput) { + return; + } + this.textInput.focus(); + }); }); } @@ -1036,6 +1043,7 @@ class ReportActionCompose extends React.Component { this.shouldBlockEmojiCalc = false; this.shouldBlockMentionCalc = false; this.setState({isAttachmentPreviewActive: false}); + this.focus(true); }} > {({displayFileInModal}) => ( @@ -1095,6 +1103,7 @@ class ReportActionCompose extends React.Component { e.preventDefault(); // Drop focus to avoid blue focus ring. + this.textInput.blur(); this.actionButton.blur(); this.setMenuVisibility(true); }} @@ -1110,7 +1119,10 @@ class ReportActionCompose extends React.Component { this.setMenuVisibility(false)} + onClose={() => { + this.setMenuVisibility(false); + this.focus(true); + }} onItemSelected={() => this.setMenuVisibility(false)} anchorPosition={styles.createMenuPositionReportActionCompose(this.props.windowHeight)} anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM}} @@ -1128,8 +1140,12 @@ class ReportActionCompose extends React.Component { this.shouldBlockMentionCalc = true; } + this.shouldFocusAfterClosingModal = false; openPicker({ onPicked: displayFileInModal, + onCanceled: () => { + this.focus(true); + }, }); }, }, From e0764460e2910a2cc247ac2568282c8d7d78f049 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Tue, 1 Aug 2023 22:08:01 +0800 Subject: [PATCH 017/214] fix lint style --- src/components/AttachmentPicker/index.js | 2 +- src/libs/ComposerFocusManager.js | 4 ++-- src/pages/home/report/ReportActionCompose.js | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/AttachmentPicker/index.js b/src/components/AttachmentPicker/index.js index d18165ab57ab..9ea94ae53d42 100644 --- a/src/components/AttachmentPicker/index.js +++ b/src/components/AttachmentPicker/index.js @@ -53,7 +53,7 @@ function AttachmentPicker(props) { if (!fileInput.current) { return; } - fileInput.current.addEventListener('cancel', () => onCanceled.current(), { once: true}); + fileInput.current.addEventListener('cancel', () => onCanceled.current(), {once: true}); }} accept={getAcceptableFileTypes(props.type)} /> diff --git a/src/libs/ComposerFocusManager.js b/src/libs/ComposerFocusManager.js index 1f5f9d2d6f97..569e165da962 100644 --- a/src/libs/ComposerFocusManager.js +++ b/src/libs/ComposerFocusManager.js @@ -2,7 +2,7 @@ let isReadyToFocusPromise = Promise.resolve(); let resolveIsReadyToFocus; function resetReadyToFocus() { - isReadyToFocusPromise = new Promise(resolve => { + isReadyToFocusPromise = new Promise((resolve) => { resolveIsReadyToFocus = resolve; }); } @@ -10,7 +10,7 @@ function setReadyToFocus() { if (!resolveIsReadyToFocus) { return; } - resolveIsReadyToFocus() + resolveIsReadyToFocus(); } function isReadyToFocus() { return isReadyToFocusPromise; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 6766cbb0e8b4..27257805244f 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -222,7 +222,7 @@ class ReportActionCompose extends React.Component { this.unsubscribeNavigationBlur = () => null; this.unsubscribeNavigationFocus = () => null; - + this.shouldFocusAfterClosingModal = true; this.state = { @@ -277,7 +277,13 @@ class ReportActionCompose extends React.Component { // We want to focus or refocus the input when a modal has been closed or the underlying screen is refocused. // We avoid doing this on native platforms since the software keyboard popping // open creates a jarring and broken UX. - if (this.willBlurTextInputOnTapOutside && this.shouldFocusAfterClosingModal && !this.props.modal.isVisible && this.props.isFocused && (prevProps.modal.isVisible || !prevProps.isFocused)) { + if ( + this.willBlurTextInputOnTapOutside && + this.shouldFocusAfterClosingModal && + !this.props.modal.isVisible && + this.props.isFocused && + (prevProps.modal.isVisible || !prevProps.isFocused) + ) { this.focus(); } From 7ac72b55865e8fd4ceb7147137c3dd5c5b07d1cc Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Thu, 3 Aug 2023 16:08:12 +0800 Subject: [PATCH 018/214] upgrade patch approach for modal --- ...-native+0.72.1+004+ModalKeyboardFlashing.patch | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch diff --git a/patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch b/patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch new file mode 100644 index 000000000000..4f239084b4ec --- /dev/null +++ b/patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/react-native/React/Views/RCTModalHostViewManager.m b/node_modules/react-native/React/Views/RCTModalHostViewManager.m +index 4b9f9ad..4992874 100644 +--- a/node_modules/react-native/React/Views/RCTModalHostViewManager.m ++++ b/node_modules/react-native/React/Views/RCTModalHostViewManager.m +@@ -79,6 +79,10 @@ RCT_EXPORT_MODULE() + if (self->_presentationBlock) { + self->_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); + } else { ++ UIWindow *window = RCTKeyWindow(); ++ if (window && window.rootViewController && [window.rootViewController.view isFirstResponder]) { ++ [window.rootViewController.view resignFirstResponder]; ++ } + [[modalHostView reactViewController] presentViewController:viewController + animated:animated + completion:completionBlock]; From ca83e138eedfe0ca3ca57f55299e5b8ab7495e36 Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Fri, 4 Aug 2023 14:57:26 +0700 Subject: [PATCH 019/214] Fix drop drag zone --- src/hooks/useDragAndDrop.js | 1 + .../ThreePaneView.js | 40 +++++++++---------- web/index.html | 10 +++++ 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/hooks/useDragAndDrop.js b/src/hooks/useDragAndDrop.js index 98df70085a72..bc8ab517731b 100644 --- a/src/hooks/useDragAndDrop.js +++ b/src/hooks/useDragAndDrop.js @@ -58,6 +58,7 @@ export default function useDragAndDrop({dropZone, onDrop = () => {}, shouldAllow } event.preventDefault(); + event.stopPropagation(); switch (event.type) { case DRAG_OVER_EVENT: diff --git a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js index 2f9a899191bf..14b78752e62f 100644 --- a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js +++ b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js @@ -53,28 +53,26 @@ function ThreePaneView(props) { ); } if (route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) { - const Wrapper = props.state.index === i ? NoDropZone : React.Fragment; return ( - - - props.navigation.goBack()} - accessibilityLabel={translate('common.close')} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} - /> - {props.descriptors[route.key].render()} - - + + props.navigation.goBack()} + accessibilityLabel={translate('common.close')} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} + /> + {props.descriptors[route.key].render()} + ); } return ( diff --git a/web/index.html b/web/index.html index d207fa54b97a..16c907fe441b 100644 --- a/web/index.html +++ b/web/index.html @@ -136,4 +136,14 @@ + From 98a5c4a0a362e45a22b77424f8e2fd599db3cc0f Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Tue, 8 Aug 2023 02:49:20 +0800 Subject: [PATCH 020/214] rename patch filename --- ....patch => react-native+0.72.1+005+ModalKeyboardFlashing.patch} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native+0.72.1+004+ModalKeyboardFlashing.patch => react-native+0.72.1+005+ModalKeyboardFlashing.patch} (100%) diff --git a/patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch b/patches/react-native+0.72.1+005+ModalKeyboardFlashing.patch similarity index 100% rename from patches/react-native+0.72.1+004+ModalKeyboardFlashing.patch rename to patches/react-native+0.72.1+005+ModalKeyboardFlashing.patch From 5ce9cf2670fd471f9424e62be53bd12251668a23 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Sat, 12 Aug 2023 03:46:36 +0530 Subject: [PATCH 021/214] fix: handle skin tones for emojis --- assets/emojis/index.js | 19 ++++++++++++++++++- src/libs/EmojiUtils.js | 6 ++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/assets/emojis/index.js b/assets/emojis/index.js index 3882ac7f0fa6..5ff2d6793f5b 100644 --- a/assets/emojis/index.js +++ b/assets/emojis/index.js @@ -27,10 +27,27 @@ const emojiCodeTable = _.reduce( {}, ); +const emojiCodeTableWithSkinTones = _.reduce( + emojis, + (prev, cur) => { + const newValue = prev; + if (!cur.header) { + newValue[cur.code] = cur; + } + if (cur.types) { + cur.types.forEach((type) => { + newValue[type] = cur; + }); + } + return newValue; + }, + {}, +); + const localeEmojis = { en: enEmojis, es: esEmojis, }; -export {emojiNameTable, emojiCodeTable, localeEmojis}; +export {emojiNameTable, emojiCodeTable, emojiCodeTableWithSkinTones, localeEmojis}; export {skinTones, categoryFrequentlyUsed, default} from './common'; diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index eda27513205e..09f03fc7d105 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -266,7 +266,7 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { - const emojis = []; + let emojis = []; if (!text) { return emojis; @@ -282,12 +282,14 @@ function extractEmojis(text) { for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; - const emoji = Emojis.emojiCodeTable[character]; + const emoji = Emojis.emojiCodeTableWithSkinTones[character]; if (emoji) { emojis.push(emoji); } } + // Create a new set to remove duplicates + emojis = [...new Set(emojis)]; return emojis; } From 4d0afeb33d67311589c50efc702f7c97b3bcff13 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Sat, 12 Aug 2023 16:43:17 +0800 Subject: [PATCH 022/214] rename patch filename --- ....patch => react-native+0.72.3+004+ModalKeyboardFlashing.patch} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native+0.72.1+005+ModalKeyboardFlashing.patch => react-native+0.72.3+004+ModalKeyboardFlashing.patch} (100%) diff --git a/patches/react-native+0.72.1+005+ModalKeyboardFlashing.patch b/patches/react-native+0.72.3+004+ModalKeyboardFlashing.patch similarity index 100% rename from patches/react-native+0.72.1+005+ModalKeyboardFlashing.patch rename to patches/react-native+0.72.3+004+ModalKeyboardFlashing.patch From 70b6f38e7e002cddaac0501379b5815d97fda95a Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 14 Aug 2023 14:53:51 +0300 Subject: [PATCH 023/214] Start creating new demo page route --- src/CONST.js | 3 ++ src/ROUTES.js | 3 ++ .../Navigators/CentralPaneNavigator.js | 10 +++++ src/libs/Navigation/linkingConfig.js | 1 + src/pages/DemoSetupPage.js | 43 +++++++++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 src/pages/DemoSetupPage.js diff --git a/src/CONST.js b/src/CONST.js index 4c19965837d9..fe0013b3e94b 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -2534,6 +2534,9 @@ const CONST = { DISTANCE: 'distance', }, STATUS_TEXT_MAX_LENGTH: 100, + DEMO_PAGES: { + SAASTR: 'SaaStrDemoSetup', + }, }; export default CONST; diff --git a/src/ROUTES.js b/src/ROUTES.js index 6c0365e40568..7f9e76a99338 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -173,6 +173,9 @@ export default { getWorkspaceTravelRoute: (policyID) => `workspace/${policyID}/travel`, getWorkspaceMembersRoute: (policyID) => `workspace/${policyID}/members`, + // These are some on-off routes that will be removed once they're no longer needed (see GH issues for details) + SAASTR: 'saastr', + /** * @param {String} route * @returns {Object} diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js index 64eadcbe06c3..47880ba14470 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator.js @@ -2,9 +2,11 @@ import React from 'react'; import {createStackNavigator} from '@react-navigation/stack'; import SCREENS from '../../../../SCREENS'; import ReportScreenWrapper from '../ReportScreenWrapper'; +import DemoSetupPage from '../../../../pages/DemoSetupPage'; import getCurrentUrl from '../../currentUrl'; import styles from '../../../../styles/styles'; import FreezeWrapper from '../../FreezeWrapper'; +import CONST from '../../../../CONST'; const Stack = createStackNavigator(); @@ -28,6 +30,14 @@ function CentralPaneNavigator() { }} component={ReportScreenWrapper} /> + ); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index dcc4f77fde73..1a2bcc164bd0 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -23,6 +23,7 @@ export default { [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID, + [CONST.DEMO_PAGES.SAASTR]: ROUTES.SAASTR }, }, [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { diff --git a/src/pages/DemoSetupPage.js b/src/pages/DemoSetupPage.js new file mode 100644 index 000000000000..d512eaff6cad --- /dev/null +++ b/src/pages/DemoSetupPage.js @@ -0,0 +1,43 @@ +import _ from 'underscore'; +import React from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; +import ONYXKEYS from '../ONYXKEYS'; +import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import CONST from '../CONST'; + +const propTypes = { + /** Navigation route context info provided by react navigation */ + route: PropTypes.shape({ + /** The exact route name used to get to this screen */ + name: PropTypes.string.isRequired, + }).isRequired, +}; + +/* + * This is a "utility page", that does this: + * - Looks at the current route + * - Determines if there's a demo command we need to call + * - If not, routes back to home + */ +function DemoSetupPage(props) { + console.log('DEMO props', props); + useFocusEffect(() => { + if (props.route.name === CONST.DEMO_PAGES.SAASTR) { + // Do SaaStr demo setup here + console.log('HERE') + } + }); + + return ; +} + +DemoSetupPage.propTypes = propTypes; +DemoSetupPage.displayName = 'DemoSetupPage'; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(DemoSetupPage); From 69da9ad36565b0948bfcc1ee815d43857e9e4d07 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 14 Aug 2023 16:31:24 +0300 Subject: [PATCH 024/214] Add new saastr email --- .env.example | 1 + src/CONST.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.env.example b/.env.example index 944da2aa9296..d3bc11ec3024 100644 --- a/.env.example +++ b/.env.example @@ -26,5 +26,6 @@ EXPENSIFY_ACCOUNT_ID_QA=-1 EXPENSIFY_ACCOUNT_ID_QA_TRAVIS=-1 EXPENSIFY_ACCOUNT_ID_RECEIPTS=-1 EXPENSIFY_ACCOUNT_ID_REWARDS=-1 +EXPENSIFY_ACCOUNT_ID_SAASTR=-1 EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR=-1 EXPENSIFY_ACCOUNT_ID_SVFG=-1 diff --git a/src/CONST.js b/src/CONST.js index fe0013b3e94b..c1125708d7f7 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -844,6 +844,7 @@ const CONST = { QA: 'qa@expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', RECEIPTS: 'receipts@expensify.com', + SAASTR: 'saastr@expensify.com', STUDENT_AMBASSADOR: 'studentambassadors@expensify.com', SVFG: 'svfg@expensify.com', }, @@ -863,6 +864,7 @@ const CONST = { QA_TRAVIS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_QA_TRAVIS', 8595733)), RECEIPTS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_RECEIPTS', -1)), REWARDS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_REWARDS', 11023767)), // rewards@expensify.com + SAASTR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SAASTR', -1)), STUDENT_AMBASSADOR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR', 10476956)), SVFG: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SVFG', 2012843)), }, From abb3964f1766e89ebc732889870a89852f2c205e Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:40:08 +0700 Subject: [PATCH 025/214] Clean import --- .../AppNavigator/createResponsiveStackNavigator/ThreePaneView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js index 14b78752e62f..75a5a1f514f7 100644 --- a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js +++ b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js @@ -11,7 +11,6 @@ import styles from '../../../../styles/styles'; import CONST from '../../../../CONST'; import PressableWithoutFeedback from '../../../../components/Pressable/PressableWithoutFeedback'; import useLocalize from '../../../../hooks/useLocalize'; -import NoDropZone from '../../../../components/DragAndDrop/NoDropZone'; const propTypes = { /* State from useNavigationBuilder */ From e805fdc56fcb428a698abd1c4630683af74bcd55 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 14 Aug 2023 16:47:49 +0300 Subject: [PATCH 026/214] Set up a new workspace, note todos --- src/libs/actions/DemoActions.js | 235 ++++++++++++++++++++++++++++++++ src/pages/DemoSetupPage.js | 6 +- 2 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 src/libs/actions/DemoActions.js diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js new file mode 100644 index 000000000000..a8529e9b1c20 --- /dev/null +++ b/src/libs/actions/DemoActions.js @@ -0,0 +1,235 @@ +import CONST from '../../CONST'; +import * as API from '../API'; +import {buildOptimisticWorkspaceChats} from '../ReportUtils'; +import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; + +function createSaastrDemoWorkspaceAndNavigate() { + // Create workspace, owned and admin'd by SaaStr + const policyID = generatePolicyID(); + + // TODO: maybe add domain name as prefix?? + const workspaceName = 'SaaStr Cantina'; + + const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); + + const { + announceChatReportID, + announceChatData, + announceReportActionData, + announceCreatedReportActionID, + // Commenting these out b/c user shouldn't an admin to start with, right? :think: + // adminsChatReportID, + // adminsChatData, + // adminsReportActionData, + // adminsCreatedReportActionID, + expenseChatReportID, + expenseChatData, + expenseReportActionData, + expenseCreatedReportActionID, + } = buildOptimisticWorkspaceChats(policyID, workspaceName); + + // TODO: Add this user to workspace as a member + // TODO: Add optimistic invite message comment + // TODO: Make sure a specific reimbursement account is tied to the workspace + // TODO: Is it fine if the expense chat report is OWNED by the user instead of saastr here? + + // should all of the "Created" report actions (for each room) be OWNED by saastr or user? + // - it shouldn't actually matter, we don't show anything + + API.write( + 'CreateSaastrDemoWorkspace', + { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID, + policyName: workspaceName, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID, + customUnitID, + customUnitRateID, + }, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.FREE, + name: workspaceName, + role: CONST.POLICY.ROLE.USER, + owner: CONST.EMAIL.SAASTR, + outputCurrency: lodashGet(allPersonalDetails, [sessionAccountID, 'localCurrencyCode'], CONST.CURRENCY.USD), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + customUnits, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: { + [sessionAccountID]: { + role: CONST.POLICY.ROLE.USER, + errors: {}, + }, + [CONST.ACCOUNT_ID.SAASTR]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...adminsChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...expenseChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: expenseReportActionData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [_.keys(announceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [_.keys(adminsChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: { + [_.keys(expenseChatData)[0]]: { + pendingAction: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: null, + }, + ], + } + ); + // TODO: navigate to workspace chat report +} + +export {createSaastrDemoWorkspaceAndNavigate}; diff --git a/src/pages/DemoSetupPage.js b/src/pages/DemoSetupPage.js index d512eaff6cad..735b91a6b1d8 100644 --- a/src/pages/DemoSetupPage.js +++ b/src/pages/DemoSetupPage.js @@ -6,6 +6,7 @@ import {useFocusEffect} from '@react-navigation/native'; import ONYXKEYS from '../ONYXKEYS'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import CONST from '../CONST'; +import {createSaastrDemoWorkspaceAndNavigate} from '../libs/actions/DemoActions'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -22,11 +23,10 @@ const propTypes = { * - If not, routes back to home */ function DemoSetupPage(props) { - console.log('DEMO props', props); useFocusEffect(() => { + // Depending on the route that the user hit to get here, run a specific demo flow if (props.route.name === CONST.DEMO_PAGES.SAASTR) { - // Do SaaStr demo setup here - console.log('HERE') + createSaastrDemoWorkspaceAndNavigate(); } }); From 49fb4c1844489c00e8d47430544b748a374f2ef5 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 14 Aug 2023 17:01:18 +0300 Subject: [PATCH 027/214] A few fixes to connect the new API command --- src/libs/actions/DemoActions.js | 31 +++++++++++++++++++++++-------- src/libs/actions/Policy.js | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index a8529e9b1c20..d671c05197c2 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -1,7 +1,19 @@ +import Onyx from 'react-native-onyx'; import CONST from '../../CONST'; import * as API from '../API'; import {buildOptimisticWorkspaceChats} from '../ReportUtils'; import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; +import Navigation from '../Navigation/Navigation'; +import ROUTES from '../../ROUTES'; +import ONYXKEYS from '../../ONYXKEYS'; + +let sessionAccountID = 0; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (val) => { + sessionAccountID = lodashGet(val, 'accountID', 0); + }, +}); function createSaastrDemoWorkspaceAndNavigate() { // Create workspace, owned and admin'd by SaaStr @@ -17,18 +29,17 @@ function createSaastrDemoWorkspaceAndNavigate() { announceChatData, announceReportActionData, announceCreatedReportActionID, - // Commenting these out b/c user shouldn't an admin to start with, right? :think: - // adminsChatReportID, - // adminsChatData, - // adminsReportActionData, - // adminsCreatedReportActionID, + // TODO: maybe comment these admin report data out b/c user shouldn't an admin to start with, right? :think: + adminsChatReportID, + adminsChatData, + adminsReportActionData, + adminsCreatedReportActionID, expenseChatReportID, expenseChatData, expenseReportActionData, expenseCreatedReportActionID, } = buildOptimisticWorkspaceChats(policyID, workspaceName); - // TODO: Add this user to workspace as a member // TODO: Add optimistic invite message comment // TODO: Make sure a specific reimbursement account is tied to the workspace // TODO: Is it fine if the expense chat report is OWNED by the user instead of saastr here? @@ -61,7 +72,7 @@ function createSaastrDemoWorkspaceAndNavigate() { name: workspaceName, role: CONST.POLICY.ROLE.USER, owner: CONST.EMAIL.SAASTR, - outputCurrency: lodashGet(allPersonalDetails, [sessionAccountID, 'localCurrencyCode'], CONST.CURRENCY.USD), + outputCurrency: CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, customUnits, }, @@ -229,7 +240,11 @@ function createSaastrDemoWorkspaceAndNavigate() { ], } ); - // TODO: navigate to workspace chat report + + // Navigate to the new workspace chat report + // We must call goBack() to remove the /saastr route from history + Navigation.goBack(); + Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policyID)); } export {createSaastrDemoWorkspaceAndNavigate}; diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 809491c14950..873eb01b17a0 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1202,4 +1202,5 @@ export { setWorkspaceInviteMembersDraft, isPolicyOwner, clearErrors, + buildOptimisticCustomUnits, }; From 420c187ac85a25b124c5f7bf0e3b1ade3712d164 Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 14 Aug 2023 20:30:18 +0530 Subject: [PATCH 028/214] chore: replace emojiCodeTable with emojiCodeTableWithSkinTones --- assets/emojis/index.js | 14 +------------- src/libs/EmojiUtils.js | 6 +++--- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/assets/emojis/index.js b/assets/emojis/index.js index 5ff2d6793f5b..c8dab36f57d9 100644 --- a/assets/emojis/index.js +++ b/assets/emojis/index.js @@ -15,18 +15,6 @@ const emojiNameTable = _.reduce( {}, ); -const emojiCodeTable = _.reduce( - emojis, - (prev, cur) => { - const newValue = prev; - if (!cur.header) { - newValue[cur.code] = cur; - } - return newValue; - }, - {}, -); - const emojiCodeTableWithSkinTones = _.reduce( emojis, (prev, cur) => { @@ -49,5 +37,5 @@ const localeEmojis = { es: esEmojis, }; -export {emojiNameTable, emojiCodeTable, emojiCodeTableWithSkinTones, localeEmojis}; +export {emojiNameTable, emojiCodeTableWithSkinTones, localeEmojis}; export {skinTones, categoryFrequentlyUsed, default} from './common'; diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 09f03fc7d105..5965b463e237 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -13,7 +13,7 @@ Onyx.connect({ key: ONYXKEYS.FREQUENTLY_USED_EMOJIS, callback: (val) => { frequentlyUsedEmojis = _.map(val, (item) => { - const emoji = Emojis.emojiCodeTable[item.code]; + const emoji = Emojis.emojiCodeTableWithSkinTones[item.code]; if (emoji) { return {...emoji, count: item.count, lastUpdatedAt: item.lastUpdatedAt}; } @@ -33,7 +33,7 @@ const findEmojiByName = (name) => Emojis.emojiNameTable[name]; * @param {String} code * @returns {Object} */ -const findEmojiByCode = (code) => Emojis.emojiCodeTable[code]; +const findEmojiByCode = (code) => Emojis.emojiCodeTableWithSkinTones[code]; /** * @@ -229,7 +229,7 @@ function getFrequentlyUsedEmojis(newEmoji) { frequentEmojiList.splice(emojiIndex, 1); } - const updatedEmoji = {...Emojis.emojiCodeTable[emoji.code], count: currentEmojiCount, lastUpdatedAt: currentTimestamp}; + const updatedEmoji = {...Emojis.emojiCodeTableWithSkinTones[emoji.code], count: currentEmojiCount, lastUpdatedAt: currentTimestamp}; // We want to make sure the current emoji is added to the list // Hence, we take one less than the current frequent used emojis From c0b42b118c7635a51440e6dc06424f3a4807b2ce Mon Sep 17 00:00:00 2001 From: jeet-dhandha Date: Mon, 14 Aug 2023 20:41:33 +0530 Subject: [PATCH 029/214] fix: logic for removing duplicates --- src/libs/EmojiUtils.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 5965b463e237..3dca56f1cba4 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -266,7 +266,7 @@ const getEmojiCodeWithSkinColor = (item, preferredSkinToneIndex) => { * @returns {Object[]} An array of emoji codes. */ function extractEmojis(text) { - let emojis = []; + const emojis = []; if (!text) { return emojis; @@ -278,18 +278,20 @@ function extractEmojis(text) { return emojis; } - parseEmojis = [...new Set(parseEmojis)]; + parseEmojis = [...new Set(parseEmojis)]; // Some emojis are repeated with different skin tones + + const alreadyParsedEmojis = new Set(); for (let i = 0; i < parseEmojis.length; i++) { const character = parseEmojis[i]; const emoji = Emojis.emojiCodeTableWithSkinTones[character]; - if (emoji) { + + if (emoji && !alreadyParsedEmojis.has(emoji.code)) { + alreadyParsedEmojis.add(emoji.code); // Add to set to avoid duplicates emojis.push(emoji); } } - // Create a new set to remove duplicates - emojis = [...new Set(emojis)]; return emojis; } From 0507a0ff97002aaa48e9ff148f2cf9add5b78d6a Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 14 Aug 2023 18:41:13 +0300 Subject: [PATCH 030/214] Add ability to run specific demo by initial URL --- src/Expensify.js | 11 +++++++++-- src/ONYXKEYS.js | 3 +++ src/languages/en.js | 5 +++++ src/languages/es.js | 5 +++++ src/libs/actions/App.js | 19 +++++++++++++++++++ src/pages/signin/SignInPage.js | 19 +++++++++++++++++-- 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index c85c2862e96e..3d6f7a861663 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -31,6 +31,7 @@ import SplashScreenHider from './components/SplashScreenHider'; import KeyboardShortcutsModal from './components/KeyboardShortcutsModal'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; import * as EmojiPickerAction from './libs/actions/EmojiPickerAction'; +import {runDemoByURL} from './libs/actions/App'; // This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection // eslint-disable-next-line no-unused-vars @@ -163,10 +164,16 @@ function Expensify(props) { appStateChangeListener.current = AppState.addEventListener('change', initializeClient); // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report - Linking.getInitialURL().then((url) => Report.openReportFromDeepLink(url, isAuthenticated)); + Linking.getInitialURL().then((url) => { + runDemoByURL(url); + Report.openReportFromDeepLink(url, isAuthenticated); + }); // Open chat report from a deep link (only mobile native) - Linking.addEventListener('url', (state) => Report.openReportFromDeepLink(state.url, isAuthenticated)); + Linking.addEventListener('url', (state) => { + runDemoByURL(state.url); + Report.openReportFromDeepLink(state.url, isAuthenticated); + }); return () => { if (!appStateChangeListener.current) { diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index a99a09063561..b6bc97c4e555 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -263,4 +263,7 @@ export default { // Receipt upload modal RECEIPT_MODAL: 'receiptModal', + + // Information on any active demos being run + DEMO_INFO: 'demoInfo', }; diff --git a/src/languages/en.js b/src/languages/en.js index 09651e6ec05e..78e8e69211d3 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -1591,4 +1591,9 @@ export default { stateSelectorModal: { placeholderText: 'Search to see options', }, + demos: { + saastr: { + signInWelcome: 'Welcome to SaaStr! Hop in to start networking now.', + } + } }; diff --git a/src/languages/es.js b/src/languages/es.js index fa920c84ce35..2af31340cf9c 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -2063,4 +2063,9 @@ export default { stateSelectorModal: { placeholderText: 'Buscar para ver opciones', }, + demos: { + saastr: { + signInWelcome: '', + } + } }; diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index fe2c908dfd0b..18f5254346f8 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -437,6 +437,24 @@ function beginDeepLinkRedirectAfterTransition() { waitForSignOnTransitionToFinish().then(beginDeepLinkRedirect); } +/** + * Runs code for specific demos, based on the provided URL + * + * @param {String} url - URL user is navigating to via deep link (or regular link in web) + */ +function runDemoByURL(url = '') { + if (url.endsWith(ROUTES.SAASTR)) { + Onyx.merge(ONYXKEYS.DEMO_INFO, { + saastr: { + isBeginningDemo: true, + } + }) + } else { + // No demo is being run, so clear out demo info + Onyx.set(ONYXKEYS.DEMO_INFO, null); + } +} + export { setLocale, setLocaleAndNavigate, @@ -449,4 +467,5 @@ export { beginDeepLinkRedirect, beginDeepLinkRedirectAfterTransition, createWorkspaceAndNavigateToIt, + runDemoByURL }; diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index f6eb4f9306f3..f46595daea60 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -45,11 +45,19 @@ const propTypes = { twoFactorAuthCode: PropTypes.string, validateCode: PropTypes.string, }), + + /** Information about any currently running demos */ + demoInfo: PropTypes.shape({ + saastr: PropTypes.shape({ + isBeginningDemo: PropTypes.bool, + }), + }), }; const defaultProps = { account: {}, credentials: {}, + demoInfo: {}, }; /** @@ -77,7 +85,7 @@ function getRenderOptions({hasLogin, hasValidateCode, hasAccount, isPrimaryLogin }; } -function SignInPage({credentials, account}) { +function SignInPage({credentials, account, demoInfo}) { const {translate, formatPhoneNumber} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const safeAreaInsets = useSafeAreaInsets(); @@ -102,7 +110,13 @@ function SignInPage({credentials, account}) { let welcomeText = ''; if (shouldShowLoginForm) { welcomeHeader = isSmallScreenWidth ? translate('login.hero.header') : translate('welcomeText.getStarted'); - welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; + + if (demoInfo.saastr && demoInfo.saastr.isBeginningDemo) { + welcomeText = translate('demos.saastr.signInWelcome'); + } else { + welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; + } + } else if (shouldShowValidateCodeForm) { if (account.requiresTwoFactorAuth) { // We will only know this after a user signs in successfully, without their 2FA code @@ -165,4 +179,5 @@ SignInPage.displayName = 'SignInPage'; export default withOnyx({ account: {key: ONYXKEYS.ACCOUNT}, credentials: {key: ONYXKEYS.CREDENTIALS}, + demoInfo: {key: ONYXKEYS.DEMO_INFO}, })(SignInPage); From 32256506be6db98e5699824bc1262e219a036be1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 13:55:27 -0600 Subject: [PATCH 031/214] Remove unused prop --- src/components/ReportActionItem/MoneyRequestPreview.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index e5a53be6c46a..85fb952a2959 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -100,9 +100,6 @@ const propTypes = { /** Information about the user accepting the terms for payments */ walletTerms: walletTermsPropTypes, - /** Pending action, if any */ - pendingAction: PropTypes.oneOf(_.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), - /** Whether or not an IOU report contains money requests in a different currency * that are either created or cancelled offline, and thus haven't been converted to the report's currency yet */ @@ -119,7 +116,6 @@ const defaultProps = { checkIfContextMenuActive: () => {}, containerStyles: [], walletTerms: {}, - pendingAction: null, isHovered: false, personalDetails: {}, session: { @@ -197,7 +193,6 @@ function MoneyRequestPreview(props) { const childContainer = ( { PaymentMethods.clearWalletTermsError(); From 648c9c2e1c4beee93537383ab7d076c663acdd64 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:00:08 -0600 Subject: [PATCH 032/214] Add transaction subscription --- src/components/ReportActionItem/MoneyRequestPreview.js | 7 +++++++ src/pages/home/report/reportActionFragmentPropTypes.js | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 85fb952a2959..85311b91073e 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -91,6 +91,9 @@ const propTypes = { }), ), + /** The transaction attached to the action.message.iouTransactionID */ + transaction: PropTypes.any, + /** Session info for the currently logged in user. */ session: PropTypes.shape({ /** Currently logged in user email */ @@ -121,6 +124,7 @@ const defaultProps = { session: { email: null, }, + transaction: null, shouldShowPendingConversionMessage: false, }; @@ -311,6 +315,9 @@ export default compose( session: { key: ONYXKEYS.SESSION, }, + transaction: { + key: ({action}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${(action && action.message && action.message.iouTransactionID) || 0}`, + }, walletTerms: { key: ONYXKEYS.WALLET_TERMS, }, diff --git a/src/pages/home/report/reportActionFragmentPropTypes.js b/src/pages/home/report/reportActionFragmentPropTypes.js index 5d2e3b951a1d..2949813dcebd 100644 --- a/src/pages/home/report/reportActionFragmentPropTypes.js +++ b/src/pages/home/report/reportActionFragmentPropTypes.js @@ -29,4 +29,7 @@ export default PropTypes.shape({ /** Fragment edited flag */ isEdited: PropTypes.bool, + + /** A possible IOU transaction ID */ + iouTransactionID: PropTypes.int, }); From d91b6947683dc96e056c2d45e19e2935e740254f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:12:19 -0600 Subject: [PATCH 033/214] Add a method to determine transaction type --- src/CONST.js | 5 +++++ src/libs/TransactionUtils.js | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index cc5d63be0373..0319ac4fad71 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -639,6 +639,11 @@ const CONST = { DARK: 'dark', SYSTEM: 'system', }, + TRANSACTION: { + TYPE: { + CUSTOM_UNIT: 'customUnit', + }, + }, JSON_CODE: { SUCCESS: 200, BAD_REQUEST: 400, diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 48892a69cc01..8a11f536b20a 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -167,4 +167,15 @@ function getCreated(transaction) { return format(new Date(lodashGet(transaction, 'created', '')), CONST.DATE.FNS_FORMAT_STRING); } -export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getTransaction, getDescription, getAmount, getCurrency, getCreated}; +/** + * @param {Object} transaction + * @param {String} transaction.type + * @param {Object} [transaction.customUnit] + * @param {String} [transaction.customUnit.name] + * @returns {Boolean} + */ +function isDistanceRequest(transaction) { + return transaction && transaction.type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && transaction.customUnit && transaction.customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE; +} + +export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getTransaction, getDescription, getAmount, getCurrency, getCreated, isDistanceRequest}; From 846176339f557a4cc4719c15a39c8538037c881d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:18:47 -0600 Subject: [PATCH 034/214] Add method for getting a receipt URL --- src/libs/ReceiptUtils.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/ReceiptUtils.js b/src/libs/ReceiptUtils.js index 635537226815..c9c6fcffa3f0 100644 --- a/src/libs/ReceiptUtils.js +++ b/src/libs/ReceiptUtils.js @@ -9,6 +9,7 @@ import ReceiptHTML from '../../assets/images/receipt-html.png'; import ReceiptDoc from '../../assets/images/receipt-doc.png'; import ReceiptGeneric from '../../assets/images/receipt-generic.png'; import ReceiptSVG from '../../assets/images/receipt-svg.png'; +import CONFIG from '../CONFIG'; function validateReceipt(file) { const {fileExtension} = FileUtils.splitExtensionFromFileName(lodashGet(file, 'name', '')); @@ -68,4 +69,13 @@ function isBeingScanned(receipt) { return receipt.state === CONST.IOU.RECEIPT_STATE.SCANREADY || receipt.state === CONST.IOU.RECEIPT_STATE.SCANNING; } -export {validateReceipt, getThumbnailAndImageURIs, isBeingScanned}; +/** + * Returns the URL of a receipt given a filename + * @param {String} filename + * @returns {String} + */ +function getURL(filename) { + return `${CONFIG.EXPENSIFY.EXPENSIFY_URL}receipts/${filename}`; +} + +export {validateReceipt, getThumbnailAndImageURIs, getURL, isBeingScanned}; From 8600f5c4aeae21e11f6b080fdb4af04eefbc923d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:32:13 -0600 Subject: [PATCH 035/214] Display header and amount --- .../ReportActionItem/MoneyRequestPreview.js | 31 +++++++++++++++---- .../report/reportActionFragmentPropTypes.js | 3 -- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 85311b91073e..06bbcc2b0e57 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -92,7 +92,18 @@ const propTypes = { ), /** The transaction attached to the action.message.iouTransactionID */ - transaction: PropTypes.any, + transaction: PropTypes.shape({ + /** The type of transaction */ + type: PropTypes.string, + + /** Custom units attached to the transaction */ + customUnits: PropTypes.arrayOf( + PropTypes.shape({ + /** The name of the custom unit */ + name: PropTypes.string, + }), + ), + }), /** Session info for the currently logged in user. */ session: PropTypes.shape({ @@ -124,7 +135,7 @@ const defaultProps = { session: { email: null, }, - transaction: null, + transaction: {}, shouldShowPendingConversionMessage: false, }; @@ -147,9 +158,9 @@ function MoneyRequestPreview(props) { const requestCurrency = moneyRequestAction.currency; const requestComment = moneyRequestAction.comment.trim(); - const transaction = ReportActionUtils.getTransaction(props.action); - const hasReceipt = TransactionUtils.hasReceipt(transaction); + const hasReceipt = TransactionUtils.hasReceipt(props.transaction); const isScanning = !ReportActionUtils.hasReadyMoneyRequests(props.action); + const isDistanceRequest = props.transaction && TransactionUtils.isDistanceRequest(props.transaction); const getSettledMessage = () => { switch (lodashGet(props.action, 'originalMessage.paymentType', '')) { @@ -169,6 +180,10 @@ function MoneyRequestPreview(props) { }; const getPreviewHeaderText = () => { + if (isDistanceRequest) { + return props.translate('tabSelector.distance'); + } + if (isScanning) { return props.translate('common.receipt'); } @@ -187,6 +202,10 @@ function MoneyRequestPreview(props) { }; const getDisplayAmountText = () => { + if (isDistanceRequest) { + return TransactionUtils.getAmount(props.transaction); + } + if (isScanning) { return props.translate('iou.receiptScanning'); } @@ -208,7 +227,7 @@ function MoneyRequestPreview(props) { {hasReceipt && ( `${ONYXKEYS.COLLECTION.TRANSACTION}${(action && action.message && action.message.iouTransactionID) || 0}`, + key: ({action}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${(action && action.originalMessage && action.originalMessage.IOUTransactionID) || 0}`, }, walletTerms: { key: ONYXKEYS.WALLET_TERMS, diff --git a/src/pages/home/report/reportActionFragmentPropTypes.js b/src/pages/home/report/reportActionFragmentPropTypes.js index 2949813dcebd..5d2e3b951a1d 100644 --- a/src/pages/home/report/reportActionFragmentPropTypes.js +++ b/src/pages/home/report/reportActionFragmentPropTypes.js @@ -29,7 +29,4 @@ export default PropTypes.shape({ /** Fragment edited flag */ isEdited: PropTypes.bool, - - /** A possible IOU transaction ID */ - iouTransactionID: PropTypes.int, }); From 613e662a74020291191d14a69f66b35b1b9726d2 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:33:30 -0600 Subject: [PATCH 036/214] Remove duplicate method --- src/libs/TransactionUtils.js | 26 +------------------------- src/pages/EditRequestPage.js | 2 +- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 8a11f536b20a..f7cdd3168f27 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -1,24 +1,10 @@ -import Onyx from 'react-native-onyx'; import {format} from 'date-fns'; import lodashGet from 'lodash/get'; import _ from 'underscore'; import CONST from '../CONST'; -import ONYXKEYS from '../ONYXKEYS'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; -let allTransactions = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (val) => { - if (!val) { - return; - } - allTransactions = val; - }, -}); - /** * Optimistically generate a transaction. * @@ -101,16 +87,6 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep return updatedTransaction; } -/** - * Retrieve the particular transaction object given its ID. - * - * @param {String} transactionID - * @returns {Object} - */ -function getTransaction(transactionID) { - return lodashGet(allTransactions, `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {}); -} - /** * Return the comment field (referred to as description in the App) from the transaction. * The comment does not have its modifiedComment counterpart. @@ -178,4 +154,4 @@ function isDistanceRequest(transaction) { return transaction && transaction.type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && transaction.customUnit && transaction.customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE; } -export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getTransaction, getDescription, getAmount, getCurrency, getCreated, isDistanceRequest}; +export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getDescription, getAmount, getCurrency, getCreated, isDistanceRequest}; diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 971ad056ae7e..941a560591c2 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -39,7 +39,7 @@ const defaultProps = { function EditRequestPage({report, route}) { const transactionID = lodashGet(ReportActionsUtils.getParentReportAction(report), 'originalMessage.IOUTransactionID', ''); - const transaction = TransactionUtils.getTransaction(transactionID); + const transaction = ReportActionsUtils.getTransaction(transactionID); const transactionDescription = TransactionUtils.getDescription(transaction); const transactionAmount = TransactionUtils.getAmount(transaction, ReportUtils.isExpenseReport(ReportUtils.getParentReport(report))); const transactionCurrency = TransactionUtils.getCurrency(transaction); From f1a0e8efd08570566f96cae03efaa4e6dc8df4c4 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 14:37:06 -0600 Subject: [PATCH 037/214] Use the right description --- .../ReportActionItem/MoneyRequestPreview.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 06bbcc2b0e57..ebe210986460 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -93,6 +93,9 @@ const propTypes = { /** The transaction attached to the action.message.iouTransactionID */ transaction: PropTypes.shape({ + /** The name of the transaction merchant */ + merchant: PropTypes.string, + /** The type of transaction */ type: PropTypes.string, @@ -156,12 +159,17 @@ function MoneyRequestPreview(props) { const requestAmount = moneyRequestAction.amount; const requestCurrency = moneyRequestAction.currency; - const requestComment = moneyRequestAction.comment.trim(); + let description = moneyRequestAction.comment.trim(); const hasReceipt = TransactionUtils.hasReceipt(props.transaction); const isScanning = !ReportActionUtils.hasReadyMoneyRequests(props.action); const isDistanceRequest = props.transaction && TransactionUtils.isDistanceRequest(props.transaction); + // On a distance request the merchange of the transaction will be used for the description since that's where it's stored in the database + if (isDistanceRequest) { + description = props.transaction.merchant; + } + const getSettledMessage = () => { switch (lodashGet(props.action, 'originalMessage.paymentType', '')) { case CONST.IOU.PAYMENT_TYPE.PAYPAL_ME: @@ -284,7 +292,7 @@ function MoneyRequestPreview(props) { {!isCurrentUserManager && props.shouldShowPendingConversionMessage && ( {props.translate('iou.pendingConversionMessage')} )} - {!_.isEmpty(requestComment) && {requestComment}} + {!_.isEmpty(description) && {description}} {props.isBillSplit && !_.isEmpty(participantAccountIDs) && ( From 58d7fe245b5249e4bc8285a832017bcb42531a84 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 14 Aug 2023 15:17:00 -0600 Subject: [PATCH 038/214] Add receipt image component --- src/components/ReportActionItem/MoneyRequestPreview.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index ebe210986460..ceffcafa89e1 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -93,6 +93,9 @@ const propTypes = { /** The transaction attached to the action.message.iouTransactionID */ transaction: PropTypes.shape({ + /** The name of the file for the receipt image. This is just the filename and doesn't come with any kind of a path */ + filename: PropTypes.string, + /** The name of the transaction merchant */ merchant: PropTypes.string, @@ -241,6 +244,8 @@ function MoneyRequestPreview(props) { hoverStyle={isScanning ? styles.moneyRequestPreviewBoxHover : undefined} /> )} + + {isDistanceRequest && } From a3f7564a22f4baf2e64fab93b69cecaa8f8e3366 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 10:17:50 +0300 Subject: [PATCH 039/214] Fix customizing the header --- src/pages/signin/SignInPage.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index f46595daea60..2617aad1d630 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -110,13 +110,11 @@ function SignInPage({credentials, account, demoInfo}) { let welcomeText = ''; if (shouldShowLoginForm) { welcomeHeader = isSmallScreenWidth ? translate('login.hero.header') : translate('welcomeText.getStarted'); + welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; if (demoInfo.saastr && demoInfo.saastr.isBeginningDemo) { - welcomeText = translate('demos.saastr.signInWelcome'); - } else { - welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; + welcomeHeader = translate('demos.saastr.signInWelcome'); } - } else if (shouldShowValidateCodeForm) { if (account.requiresTwoFactorAuth) { // We will only know this after a user signs in successfully, without their 2FA code From 24843e6251e31d967e123b17a31584a38ac28c5a Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 11:30:40 +0300 Subject: [PATCH 040/214] New util to get workspace chat report from policy owner --- src/libs/ReportUtils.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index ab8537d2bcdd..951e93fa99c3 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2884,6 +2884,19 @@ function getPolicy(policyID) { return policy; } +/** + * @param {String} policyOwner + * @returns {String|null} + */ +function getPolicyExpenseChatReportIDByOwner(policyOwner) { + const policy = _.find(allPolicies, (policy) => policy.owner === policyOwner); + if (!policy) { + return null; + } + + return _find(allReports, (report) => isPolicyExpenseChat(report) && report.policyID === policy.id); +} + /* * @param {Object|null} report * @returns {Boolean} @@ -3148,6 +3161,7 @@ export { getReportOfflinePendingActionAndErrors, isDM, getPolicy, + getPolicyExpenseChatReportIDByOwner, shouldDisableSettings, shouldDisableRename, hasSingleParticipant, From 247a58ac87cfa8d17666aed3b1f23e302c00c04b Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 11:31:04 +0300 Subject: [PATCH 041/214] Lookup workspace chat first & add workspace name prefix --- src/libs/actions/DemoActions.js | 34 +++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index d671c05197c2..fe237f5a1b25 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -1,26 +1,52 @@ +import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import CONST from '../../CONST'; import * as API from '../API'; -import {buildOptimisticWorkspaceChats} from '../ReportUtils'; +import {buildOptimisticWorkspaceChats, getPolicyExpenseChatReportIDByOwner} from '../ReportUtils'; import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; import ONYXKEYS from '../../ONYXKEYS'; let sessionAccountID = 0; +let sessionEmail = 0; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionAccountID = lodashGet(val, 'accountID', 0); + sessionEmail = lodashGet(val, 'email', ''); }, }); +let userIsFromPublicDomain; +Onyx.connect({ + key: ONYXKEYS.USER, + callback: (val) => { + if (!val) { + return; + } + + userIsFromPublicDomain = val.isFromPublicDomain; + } +}) + function createSaastrDemoWorkspaceAndNavigate() { + // Try to navigate to existing SaaStr expense chat if it exists in Onyx + const saastrWorkspaceChatReportID = getPolicyExpenseChatReportIDByOwner(CONST.EMAIL.SAASTR); + if (saastrWorkspaceChatReportID) { + // We must call goBack() to remove the /saastr route from history + Navigation.goBack(); + Navigation.navigate(ROUTES.getReportRoute(saastrWorkspaceChatReportID)); + return; + } + // Create workspace, owned and admin'd by SaaStr const policyID = generatePolicyID(); - // TODO: maybe add domain name as prefix?? - const workspaceName = 'SaaStr Cantina'; + // Add domain name as prefix (only if domain is private) + const domainNamePrefix = userIsFromPublicDomain ? `${Str.extractEmailDomain(sessionEmail)} ` : ''; + const workspaceName = `${domainNamePrefix}SaaStr Workspace`; const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); @@ -244,7 +270,7 @@ function createSaastrDemoWorkspaceAndNavigate() { // Navigate to the new workspace chat report // We must call goBack() to remove the /saastr route from history Navigation.goBack(); - Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policyID)); + Navigation.navigate(ROUTES.getReportRoute(expenseChatReportID)); } export {createSaastrDemoWorkspaceAndNavigate}; From 0b32e91742d13dc5b63d7a4beb76430492ed5341 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 11:59:34 +0300 Subject: [PATCH 042/214] Add optimistic welcome message when creating workspace chat --- src/libs/actions/DemoActions.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index fe237f5a1b25..6f2ad6ba2f7d 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -3,7 +3,7 @@ import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; import CONST from '../../CONST'; import * as API from '../API'; -import {buildOptimisticWorkspaceChats, getPolicyExpenseChatReportIDByOwner} from '../ReportUtils'; +import {buildOptimisticWorkspaceChats, buildOptimisticAddCommentReportAction, getPolicyExpenseChatReportIDByOwner} from '../ReportUtils'; import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; @@ -66,7 +66,29 @@ function createSaastrDemoWorkspaceAndNavigate() { expenseCreatedReportActionID, } = buildOptimisticWorkspaceChats(policyID, workspaceName); - // TODO: Add optimistic invite message comment + // Add optimistic invite message comment + const initialMessageText = ` + Welcome to NewExpensify, who wants $20? + + To scan the receipt and get paid: + 1. Click the + + 2. Click Request Money. + 3. Take a photo of the receipt, we'll automatically enter all the info. + 4. Come say hi at the Expensify booth (#601) and let us know if you have any feedback! + `; + // TODO: make sure this comes FROM saastr!! + const welcomeMessageReportAction = buildOptimisticAddCommentReportAction(initialMessageText); + + // Update policy expense chat report actions with welcome message from saastr + expenseReportActionData[welcomeMessageReportAction.reportAction.reportActionID] = welcomeMessageReportAction.reportAction; + + // Update report with info about last message sent + const currentTime = DateUtils.getDBTime(); + expenseChatData.lastVisibleActionCreated = currentTime, + expenseChatData.lastMessageText = initialMessageText, + expenseChatData.lastActorAccountID = CONST.ACCOUNT_ID.SAASTR, + expenseChatData.lastReadTime = currentTime, + // TODO: Make sure a specific reimbursement account is tied to the workspace // TODO: Is it fine if the expense chat report is OWNED by the user instead of saastr here? From 24f4e367c28d3f63de0f1f5e5d7775df61e7ea62 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 12:05:56 +0300 Subject: [PATCH 043/214] A few fixes & remove a few TODOs --- src/libs/actions/DemoActions.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index 6f2ad6ba2f7d..5eeabac77142 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -84,16 +84,10 @@ function createSaastrDemoWorkspaceAndNavigate() { // Update report with info about last message sent const currentTime = DateUtils.getDBTime(); - expenseChatData.lastVisibleActionCreated = currentTime, - expenseChatData.lastMessageText = initialMessageText, - expenseChatData.lastActorAccountID = CONST.ACCOUNT_ID.SAASTR, - expenseChatData.lastReadTime = currentTime, - - // TODO: Make sure a specific reimbursement account is tied to the workspace - // TODO: Is it fine if the expense chat report is OWNED by the user instead of saastr here? - - // should all of the "Created" report actions (for each room) be OWNED by saastr or user? - // - it shouldn't actually matter, we don't show anything + expenseChatData.lastVisibleActionCreated = currentTime; + expenseChatData.lastMessageText = initialMessageText; + expenseChatData.lastActorAccountID = CONST.ACCOUNT_ID.SAASTR; + expenseChatData.lastReadTime = currentTime; API.write( 'CreateSaastrDemoWorkspace', From 2556d2f2e8013e94a536fa0629f3ae37b01a0f11 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 12:15:30 +0300 Subject: [PATCH 044/214] Make sure welcome message comes from SaaStr --- src/libs/actions/DemoActions.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index 5eeabac77142..fef5e765ef1d 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -26,7 +26,6 @@ Onyx.connect({ if (!val) { return; } - userIsFromPublicDomain = val.isFromPublicDomain; } }) @@ -76,8 +75,10 @@ function createSaastrDemoWorkspaceAndNavigate() { 3. Take a photo of the receipt, we'll automatically enter all the info. 4. Come say hi at the Expensify booth (#601) and let us know if you have any feedback! `; - // TODO: make sure this comes FROM saastr!! const welcomeMessageReportAction = buildOptimisticAddCommentReportAction(initialMessageText); + welcomeMessageReportAction.reportAction.actorAccountID = CONST.ACCOUNT_ID.SAASTR; + welcomeMessageReportAction.reportAction.person[0].text = 'SaaStr 2023'; + welcomeMessageReportAction.reportAction.avatar = ''; // Update policy expense chat report actions with welcome message from saastr expenseReportActionData[welcomeMessageReportAction.reportAction.reportActionID] = welcomeMessageReportAction.reportAction; From e812d10a11291a5e417ec7303f2ba0ca22707834 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 12:18:26 +0300 Subject: [PATCH 045/214] Add saastr acct num --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index e35926a78fc7..e4715eb43a9c 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -865,7 +865,7 @@ const CONST = { QA_TRAVIS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_QA_TRAVIS', 8595733)), RECEIPTS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_RECEIPTS', -1)), REWARDS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_REWARDS', 11023767)), // rewards@expensify.com - SAASTR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SAASTR', -1)), + SAASTR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SAASTR', 15252830)), STUDENT_AMBASSADOR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR', 10476956)), SVFG: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SVFG', 2012843)), }, From 2d3d3a3e2b5f8a46a158853e2ec159703cfdb80a Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 12:25:46 +0300 Subject: [PATCH 046/214] prettify --- src/languages/en.js | 4 ++-- src/languages/es.js | 4 ++-- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/actions/App.js | 6 +++--- src/libs/actions/DemoActions.js | 6 +++--- src/pages/signin/SignInPage.js | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index ee6a5893019c..c15e0a66b387 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -1595,6 +1595,6 @@ export default { demos: { saastr: { signInWelcome: 'Welcome to SaaStr! Hop in to start networking now.', - } - } + }, + }, }; diff --git a/src/languages/es.js b/src/languages/es.js index 7511ec1e17e5..c150972aa750 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -2067,6 +2067,6 @@ export default { demos: { saastr: { signInWelcome: '', - } - } + }, + }, }; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 1a2bcc164bd0..d583d9c5bc13 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -23,7 +23,7 @@ export default { [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID, - [CONST.DEMO_PAGES.SAASTR]: ROUTES.SAASTR + [CONST.DEMO_PAGES.SAASTR]: ROUTES.SAASTR, }, }, [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index b7ae9f7272d6..41b617379664 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -447,8 +447,8 @@ function runDemoByURL(url = '') { Onyx.merge(ONYXKEYS.DEMO_INFO, { saastr: { isBeginningDemo: true, - } - }) + }, + }); } else { // No demo is being run, so clear out demo info Onyx.set(ONYXKEYS.DEMO_INFO, null); @@ -467,5 +467,5 @@ export { beginDeepLinkRedirect, beginDeepLinkRedirectAfterTransition, createWorkspaceAndNavigateToIt, - runDemoByURL + runDemoByURL, }; diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index fef5e765ef1d..72169a7ce2c8 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -27,8 +27,8 @@ Onyx.connect({ return; } userIsFromPublicDomain = val.isFromPublicDomain; - } -}) + }, +}); function createSaastrDemoWorkspaceAndNavigate() { // Try to navigate to existing SaaStr expense chat if it exists in Onyx @@ -281,7 +281,7 @@ function createSaastrDemoWorkspaceAndNavigate() { value: null, }, ], - } + }, ); // Navigate to the new workspace chat report diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index 2617aad1d630..a19d1dea5faf 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -111,7 +111,7 @@ function SignInPage({credentials, account, demoInfo}) { if (shouldShowLoginForm) { welcomeHeader = isSmallScreenWidth ? translate('login.hero.header') : translate('welcomeText.getStarted'); welcomeText = isSmallScreenWidth ? translate('welcomeText.getStarted') : ''; - + if (demoInfo.saastr && demoInfo.saastr.isBeginningDemo) { welcomeHeader = translate('demos.saastr.signInWelcome'); } From 4cf57058833d40d672b813bfc01a35a99f7da290 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 15:39:52 +0300 Subject: [PATCH 047/214] Update spanish translation --- src/languages/es.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.js b/src/languages/es.js index c150972aa750..20e215a18fe2 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -2066,7 +2066,7 @@ export default { }, demos: { saastr: { - signInWelcome: '', + signInWelcome: 'ยกBienvenido a SaaStr! Entra y empieza a establecer contactos.', }, }, }; From c9e2d65230b22425ee9b3dda952e68b147c11ef6 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 16:58:28 +0300 Subject: [PATCH 048/214] Remove policyExpenseChat beta --- src/CONST.js | 1 - src/libs/Permissions.js | 8 -------- src/libs/ReportUtils.js | 5 ----- src/libs/actions/Policy.js | 5 ----- 4 files changed, 19 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index a0c6cbf2bcf3..3c531c2a257c 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -216,7 +216,6 @@ const CONST = { INTERNATIONALIZATION: 'internationalization', IOU_SEND: 'sendMoney', POLICY_ROOMS: 'policyRooms', - POLICY_EXPENSE_CHAT: 'policyExpenseChat', PASSWORDLESS: 'passwordless', TASKS: 'tasks', THREADS: 'threads', diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js index f80f1878fc36..b490d8cbe367 100644 --- a/src/libs/Permissions.js +++ b/src/libs/Permissions.js @@ -70,14 +70,6 @@ function canUsePolicyRooms(betas) { return _.contains(betas, CONST.BETAS.POLICY_ROOMS) || canUseAllBetas(betas); } -/** - * @param {Array} betas - * @returns {Boolean} - */ -function canUsePolicyExpenseChat(betas) { - return _.contains(betas, CONST.BETAS.POLICY_EXPENSE_CHAT) || canUseAllBetas(betas); -} - /** * @param {Array} betas * @returns {Boolean} diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index aa72fc88d8cc..2a92e028c0df 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2530,11 +2530,6 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, iouRep return true; } - // Exclude policy expense chats if the user isn't in the policy expense chat beta - if (isPolicyExpenseChat(report) && !Permissions.canUsePolicyExpenseChat(betas)) { - return false; - } - // Hide chats between two users that haven't been commented on from the LNH if (excludeEmptyChats && isEmptyChat && isChatReport(report) && !isChatRoom(report) && !isPolicyExpenseChat(report)) { return false; diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 809491c14950..22b729b25581 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -261,11 +261,6 @@ function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, betas) { reportCreationData: {}, }; - // If the user is not in the beta, we don't want to create any chats - if (!Permissions.canUsePolicyExpenseChat(betas)) { - return workspaceMembersChats; - } - _.each(invitedEmailsToAccountIDs, (accountID, email) => { const cleanAccountID = Number(accountID); const login = OptionsListUtils.addSMSDomainIfPhoneNumber(email); From 109960015984b1c2a7040db65756f92a0650759e Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 15 Aug 2023 16:58:35 +0300 Subject: [PATCH 049/214] Update tests --- src/libs/__mocks__/Permissions.js | 1 - tests/unit/SidebarFilterTest.js | 64 ++++--------------------------- tests/unit/SidebarOrderTest.js | 4 +- tests/unit/SidebarTest.js | 4 +- 4 files changed, 12 insertions(+), 61 deletions(-) diff --git a/src/libs/__mocks__/Permissions.js b/src/libs/__mocks__/Permissions.js index 5486d184d51b..3d7a94b86848 100644 --- a/src/libs/__mocks__/Permissions.js +++ b/src/libs/__mocks__/Permissions.js @@ -12,6 +12,5 @@ export default { ...jest.requireActual('../Permissions'), canUseDefaultRooms: (betas) => _.contains(betas, CONST.BETAS.DEFAULT_ROOMS), canUsePolicyRooms: (betas) => _.contains(betas, CONST.BETAS.POLICY_ROOMS), - canUsePolicyExpenseChat: (betas) => _.contains(betas, CONST.BETAS.POLICY_EXPENSE_CHAT), canUseIOUSend: (betas) => _.contains(betas, CONST.BETAS.IOU_SEND), }; diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.js index bcc8d2f8765c..61dac3f2f786 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.js @@ -127,54 +127,6 @@ describe('Sidebar', () => { ); }); - it('includes or excludes policy expensechats depending on the beta', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - - // Given a policy expense report - // and the user not being in any betas - const report = { - ...LHNTestUtils.getFakeReport(), - chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, - }; - - return ( - waitForPromisesToResolve() - // When Onyx is updated to contain that data and the sidebar re-renders - .then(() => - Onyx.multiSet({ - [ONYXKEYS.BETAS]: [], - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - }), - ) - - // When the report has at least one ADDCOMMENT action to be rendered in the LNH - .then(() => Report.addComment(report.reportID, 'Hi, this is a comment')) - - // Then no reports are rendered in the LHN - .then(() => { - const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(hintText); - expect(optionRows).toHaveLength(0); - }) - - // When the user is added to the policy expense beta and the sidebar re-renders - .then(() => - Onyx.multiSet({ - [ONYXKEYS.BETAS]: [CONST.BETAS.POLICY_EXPENSE_CHAT], - }), - ) - - // Then there is one report rendered in the LHN - .then(() => { - const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(hintText); - expect(optionRows).toHaveLength(1); - }) - ); - }); - it('includes or excludes user created policy rooms depending on the beta', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); @@ -335,7 +287,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; // Given there are 6 boolean variables tested in the filtering logic: // 1. isArchived @@ -539,7 +491,7 @@ describe('Sidebar', () => { }; LHNTestUtils.getDefaultRenderedSidebarLinks(); - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() @@ -602,7 +554,7 @@ describe('Sidebar', () => { }; LHNTestUtils.getDefaultRenderedSidebarLinks(); - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() @@ -660,7 +612,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; // Given there are 6 boolean variables tested in the filtering logic: // 1. isArchived @@ -753,7 +705,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() @@ -804,7 +756,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() @@ -853,7 +805,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() @@ -898,7 +850,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; return ( waitForPromisesToResolve() diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 734f062ec770..95277ec74ffe 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -575,7 +575,7 @@ describe('Sidebar', () => { Report.addComment(report3.reportID, 'Hi, this is a comment'); // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForPromisesToResolve() @@ -667,7 +667,7 @@ describe('Sidebar', () => { const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForPromisesToResolve() diff --git a/tests/unit/SidebarTest.js b/tests/unit/SidebarTest.js index 3a89c5564bc5..84403ce5fc11 100644 --- a/tests/unit/SidebarTest.js +++ b/tests/unit/SidebarTest.js @@ -56,7 +56,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForPromisesToResolve() @@ -99,7 +99,7 @@ describe('Sidebar', () => { }; // Given the user is in all betas - const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; + const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS]; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForPromisesToResolve() From 8f7ab26d9744812b97f0af1a8e91579eac436fc2 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 15 Aug 2023 13:22:47 -0400 Subject: [PATCH 050/214] add distance menu item --- src/components/ReportActionItem/MoneyRequestView.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index b01226df0d35..5386038090da 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -94,6 +94,8 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic receiptUris = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); } + const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); + return ( @@ -128,6 +130,15 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic shouldShowRightIcon={canEdit} onPress={() => Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))} /> + {isDistanceRequest && ( + {}} + /> + )} {shouldShowHorizontalRule && } ); From 5c64db923e97377357eab63880aa12a501452911 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 15 Aug 2023 13:26:46 -0400 Subject: [PATCH 051/214] get custom unit and mileage rate and use distance text stub --- .../ReportActionItem/MoneyRequestView.js | 5 ++++- src/libs/PolicyUtils.js | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 5386038090da..abbe30ab895f 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -23,6 +23,7 @@ import * as CurrencyUtils from '../../libs/CurrencyUtils'; import EmptyStateBackgroundImage from '../../../assets/images/empty-state_background-fade.png'; import useLocalize from '../../hooks/useLocalize'; import * as TransactionUtils from '../../libs/TransactionUtils'; +import * as PolicyUtils from '../../libs/PolicyUtils'; import * as ReceiptUtils from '../../libs/ReceiptUtils'; import withLocalize from '../withLocalize'; import useWindowDimensions from '../../hooks/useWindowDimensions'; @@ -95,6 +96,8 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic } const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); + const distanceCustomUnit = PolicyUtils.getDistanceCustomUnit(policy); + const mileageRate = PolicyUtils.getDefaultMileageRate(distanceCustomUnit); return ( @@ -133,7 +136,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic {isDistanceRequest && ( {}} diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 582271d6610e..7be02703415d 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -166,6 +166,23 @@ function getIneligibleInvitees(policyMembers, personalDetails) { return memberEmailsToExclude; } +function getDistanceCustomUnit(policy) { + const customUnits = _.values(policy.customUnits); + return _.find(customUnits, (customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); +} + +function getDefaultMileageRate(customUnit) { + if (!customUnit) { + return 0; + } + + return _.find(customUnit.rates, (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); +} + +function getDistanceText(transaction, customUnit, mileageRate) { + return '123 miles @ $456 / mile'; +} + export { getActivePolicies, hasPolicyMemberError, @@ -179,4 +196,7 @@ export { isPolicyAdmin, getMemberAccountIDsForWorkspace, getIneligibleInvitees, + getDistanceCustomUnit, + getDefaultMileageRate, + getDistanceText, }; From 8de53ea30399e1f4de378cf5111f566f562cf850 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 15 Aug 2023 13:35:59 -0400 Subject: [PATCH 052/214] add isDistanceRequest stub --- src/libs/TransactionUtils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 48892a69cc01..92f9b74e1eb0 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -167,4 +167,8 @@ function getCreated(transaction) { return format(new Date(lodashGet(transaction, 'created', '')), CONST.DATE.FNS_FORMAT_STRING); } -export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getTransaction, getDescription, getAmount, getCurrency, getCreated}; +function isDistanceRequest(transaction) { + return true; +} + +export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getTransaction, getDescription, getAmount, getCurrency, getCreated, isDistanceRequest}; From b7be1f207ca3f6bd025bc29dd23e91f7c1c5e5fa Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Wed, 16 Aug 2023 00:51:53 +0700 Subject: [PATCH 053/214] Move to App.js --- src/App.js | 21 ++++++++++++++++++++- web/index.html | 10 ---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/App.js b/src/App.js index d8faa911f86b..bb6473587e1d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,5 @@ import '../wdyr'; -import React from 'react'; +import React, {useEffect} from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {SafeAreaProvider} from 'react-native-safe-area-context'; @@ -23,6 +23,8 @@ import ThemeStylesProvider from './styles/ThemeStylesProvider'; import {CurrentReportIDContextProvider} from './components/withCurrentReportID'; import {EnvironmentProvider} from './components/withEnvironment'; import * as Session from './libs/actions/Session'; +import getPlatform from './libs/getPlatform'; +import CONST from './CONST'; // For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx if (window && Environment.isDevelopment()) { @@ -40,6 +42,23 @@ LogBox.ignoreLogs([ const fill = {flex: 1}; function App() { + const dropDragListener = (event) => { + event.preventDefault(); + // eslint-disable-next-line no-param-reassign + event.dataTransfer.dropEffect = 'none'; + }; + + useEffect(() => { + const platform = getPlatform(); + if (platform !== CONST.PLATFORM.WEB && platform !== CONST.PLATFORM.DESKTOP) { + return; + } + document.addEventListener('dragover', dropDragListener); + document.addEventListener('dragenter', dropDragListener); + document.addEventListener('dragleave', dropDragListener); + document.addEventListener('drop', dropDragListener); + }, []); + return ( - From 018a53d5ab1cd6bed68771568c0ce61589bef841 Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:13:54 +0700 Subject: [PATCH 054/214] Add setDefaultDragDropEvent --- src/App.js | 18 ++---------------- src/libs/setDefaultDragDropEvent/index.js | 13 +++++++++++++ .../setDefaultDragDropEvent/index.native.js | 1 + 3 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 src/libs/setDefaultDragDropEvent/index.js create mode 100644 src/libs/setDefaultDragDropEvent/index.native.js diff --git a/src/App.js b/src/App.js index bb6473587e1d..d4944bbc6a0a 100644 --- a/src/App.js +++ b/src/App.js @@ -23,8 +23,7 @@ import ThemeStylesProvider from './styles/ThemeStylesProvider'; import {CurrentReportIDContextProvider} from './components/withCurrentReportID'; import {EnvironmentProvider} from './components/withEnvironment'; import * as Session from './libs/actions/Session'; -import getPlatform from './libs/getPlatform'; -import CONST from './CONST'; +import setDefaultDragDropEvent from './libs/setDefaultDragDropEvent'; // For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx if (window && Environment.isDevelopment()) { @@ -42,21 +41,8 @@ LogBox.ignoreLogs([ const fill = {flex: 1}; function App() { - const dropDragListener = (event) => { - event.preventDefault(); - // eslint-disable-next-line no-param-reassign - event.dataTransfer.dropEffect = 'none'; - }; - useEffect(() => { - const platform = getPlatform(); - if (platform !== CONST.PLATFORM.WEB && platform !== CONST.PLATFORM.DESKTOP) { - return; - } - document.addEventListener('dragover', dropDragListener); - document.addEventListener('dragenter', dropDragListener); - document.addEventListener('dragleave', dropDragListener); - document.addEventListener('drop', dropDragListener); + setDefaultDragDropEvent(); }, []); return ( diff --git a/src/libs/setDefaultDragDropEvent/index.js b/src/libs/setDefaultDragDropEvent/index.js new file mode 100644 index 000000000000..b0e411a5fb7b --- /dev/null +++ b/src/libs/setDefaultDragDropEvent/index.js @@ -0,0 +1,13 @@ +function dragDropListener(event) { + event.preventDefault(); + // eslint-disable-next-line no-param-reassign + event.dataTransfer.dropEffect = 'none'; +} + +function setDefaultDragDropEvent() { + document.addEventListener('dragover', dragDropListener); + document.addEventListener('dragenter', dragDropListener); + document.addEventListener('dragleave', dragDropListener); + document.addEventListener('drop', dragDropListener); +} +export default setDefaultDragDropEvent; diff --git a/src/libs/setDefaultDragDropEvent/index.native.js b/src/libs/setDefaultDragDropEvent/index.native.js new file mode 100644 index 000000000000..2d1ec238274a --- /dev/null +++ b/src/libs/setDefaultDragDropEvent/index.native.js @@ -0,0 +1 @@ +export default () => {}; From acdf45df70559a71afdf6799a4ebc1eba008efb2 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 15 Aug 2023 14:15:46 -0400 Subject: [PATCH 055/214] use transaction merchant instead of recomputing the distance text --- .../ReportActionItem/MoneyRequestView.js | 5 +---- src/libs/PolicyUtils.js | 20 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index abbe30ab895f..4db4fea47472 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -23,7 +23,6 @@ import * as CurrencyUtils from '../../libs/CurrencyUtils'; import EmptyStateBackgroundImage from '../../../assets/images/empty-state_background-fade.png'; import useLocalize from '../../hooks/useLocalize'; import * as TransactionUtils from '../../libs/TransactionUtils'; -import * as PolicyUtils from '../../libs/PolicyUtils'; import * as ReceiptUtils from '../../libs/ReceiptUtils'; import withLocalize from '../withLocalize'; import useWindowDimensions from '../../hooks/useWindowDimensions'; @@ -96,8 +95,6 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic } const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const distanceCustomUnit = PolicyUtils.getDistanceCustomUnit(policy); - const mileageRate = PolicyUtils.getDefaultMileageRate(distanceCustomUnit); return ( @@ -136,7 +133,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic {isDistanceRequest && ( {}} diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 7be02703415d..582271d6610e 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -166,23 +166,6 @@ function getIneligibleInvitees(policyMembers, personalDetails) { return memberEmailsToExclude; } -function getDistanceCustomUnit(policy) { - const customUnits = _.values(policy.customUnits); - return _.find(customUnits, (customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); -} - -function getDefaultMileageRate(customUnit) { - if (!customUnit) { - return 0; - } - - return _.find(customUnit.rates, (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); -} - -function getDistanceText(transaction, customUnit, mileageRate) { - return '123 miles @ $456 / mile'; -} - export { getActivePolicies, hasPolicyMemberError, @@ -196,7 +179,4 @@ export { isPolicyAdmin, getMemberAccountIDsForWorkspace, getIneligibleInvitees, - getDistanceCustomUnit, - getDefaultMileageRate, - getDistanceText, }; From ced727a73aa6057062d17775b1dca2ab57b2dae6 Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:34:42 +0700 Subject: [PATCH 056/214] Add useDefaultDragAndDrop --- src/App.js | 9 +++------ src/hooks/useDefaultDragAndDrop/index.js | 16 ++++++++++++++++ .../useDefaultDragAndDrop}/index.native.js | 0 src/libs/setDefaultDragDropEvent/index.js | 13 ------------- 4 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 src/hooks/useDefaultDragAndDrop/index.js rename src/{libs/setDefaultDragDropEvent => hooks/useDefaultDragAndDrop}/index.native.js (100%) delete mode 100644 src/libs/setDefaultDragDropEvent/index.js diff --git a/src/App.js b/src/App.js index d4944bbc6a0a..c432a0b666c8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,5 @@ import '../wdyr'; -import React, {useEffect} from 'react'; +import React from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {SafeAreaProvider} from 'react-native-safe-area-context'; @@ -23,7 +23,7 @@ import ThemeStylesProvider from './styles/ThemeStylesProvider'; import {CurrentReportIDContextProvider} from './components/withCurrentReportID'; import {EnvironmentProvider} from './components/withEnvironment'; import * as Session from './libs/actions/Session'; -import setDefaultDragDropEvent from './libs/setDefaultDragDropEvent'; +import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; // For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx if (window && Environment.isDevelopment()) { @@ -41,10 +41,7 @@ LogBox.ignoreLogs([ const fill = {flex: 1}; function App() { - useEffect(() => { - setDefaultDragDropEvent(); - }, []); - + useDefaultDragAndDrop(); return ( { + event.preventDefault(); + // eslint-disable-next-line no-param-reassign + event.dataTransfer.dropEffect = 'none'; + }; + + useEffect(() => { + document.addEventListener('dragover', dropDragListener); + document.addEventListener('dragenter', dropDragListener); + document.addEventListener('dragleave', dropDragListener); + document.addEventListener('drop', dropDragListener); + }, []); +} diff --git a/src/libs/setDefaultDragDropEvent/index.native.js b/src/hooks/useDefaultDragAndDrop/index.native.js similarity index 100% rename from src/libs/setDefaultDragDropEvent/index.native.js rename to src/hooks/useDefaultDragAndDrop/index.native.js diff --git a/src/libs/setDefaultDragDropEvent/index.js b/src/libs/setDefaultDragDropEvent/index.js deleted file mode 100644 index b0e411a5fb7b..000000000000 --- a/src/libs/setDefaultDragDropEvent/index.js +++ /dev/null @@ -1,13 +0,0 @@ -function dragDropListener(event) { - event.preventDefault(); - // eslint-disable-next-line no-param-reassign - event.dataTransfer.dropEffect = 'none'; -} - -function setDefaultDragDropEvent() { - document.addEventListener('dragover', dragDropListener); - document.addEventListener('dragenter', dragDropListener); - document.addEventListener('dragleave', dragDropListener); - document.addEventListener('drop', dragDropListener); -} -export default setDefaultDragDropEvent; From 7db1ba678b01602cfb184a668acc7270b79240ca Mon Sep 17 00:00:00 2001 From: ginsuma <13113013+ginsuma@users.noreply.github.com> Date: Wed, 16 Aug 2023 01:53:38 +0700 Subject: [PATCH 057/214] Update useDefaultDragAndDrop --- src/hooks/useDefaultDragAndDrop/index.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/hooks/useDefaultDragAndDrop/index.js b/src/hooks/useDefaultDragAndDrop/index.js index f677209afeca..34005c9de2b3 100644 --- a/src/hooks/useDefaultDragAndDrop/index.js +++ b/src/hooks/useDefaultDragAndDrop/index.js @@ -1,16 +1,21 @@ import {useEffect} from 'react'; export default function useDefaultDragAndDrop() { - const dropDragListener = (event) => { - event.preventDefault(); - // eslint-disable-next-line no-param-reassign - event.dataTransfer.dropEffect = 'none'; - }; - useEffect(() => { + const dropDragListener = (event) => { + event.preventDefault(); + // eslint-disable-next-line no-param-reassign + event.dataTransfer.dropEffect = 'none'; + }; document.addEventListener('dragover', dropDragListener); document.addEventListener('dragenter', dropDragListener); document.addEventListener('dragleave', dropDragListener); document.addEventListener('drop', dropDragListener); + return () => { + document.removeEventListener('dragover', dropDragListener); + document.removeEventListener('dragenter', dropDragListener); + document.removeEventListener('dragleave', dropDragListener); + document.removeEventListener('drop', dropDragListener); + }; }, []); } From 9be4aee96693e77de1ce0ef9d0ca8b58694acb6d Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 16 Aug 2023 04:58:38 +0300 Subject: [PATCH 058/214] Small tweaks --- src/libs/ReportUtils.js | 2 +- src/libs/actions/DemoActions.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index cb607fc9255c..a708c324b3ca 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3020,7 +3020,7 @@ function getPolicyExpenseChatReportIDByOwner(policyOwner) { return null; } - return _find(allReports, (report) => isPolicyExpenseChat(report) && report.policyID === policy.id); + return _.find(allReports, (report) => isPolicyExpenseChat(report) && report.policyID === policy.id); } /* diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index 72169a7ce2c8..1029a92252d4 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -8,6 +8,7 @@ import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; import ONYXKEYS from '../../ONYXKEYS'; +import DateUtils from '../DateUtils'; let sessionAccountID = 0; let sessionEmail = 0; From d4b812ee3a250c8d379de58f348dfc1a6e6b8247 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 15 Aug 2023 20:51:16 -0600 Subject: [PATCH 059/214] Properly format currency and images --- .../ReportActionItem/MoneyRequestPreview.js | 4 ++-- src/libs/ReceiptUtils.js | 11 +---------- src/libs/TransactionUtils.js | 4 +++- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index ceffcafa89e1..0c111f6809c4 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -214,7 +214,7 @@ function MoneyRequestPreview(props) { const getDisplayAmountText = () => { if (isDistanceRequest) { - return TransactionUtils.getAmount(props.transaction); + return CurrencyUtils.convertToDisplayString(TransactionUtils.getAmount(props.transaction), props.transaction.currency); } if (isScanning) { @@ -245,7 +245,7 @@ function MoneyRequestPreview(props) { /> )} - {isDistanceRequest && } + {isDistanceRequest && } diff --git a/src/libs/ReceiptUtils.js b/src/libs/ReceiptUtils.js index c9c6fcffa3f0..03781db0fa31 100644 --- a/src/libs/ReceiptUtils.js +++ b/src/libs/ReceiptUtils.js @@ -69,13 +69,4 @@ function isBeingScanned(receipt) { return receipt.state === CONST.IOU.RECEIPT_STATE.SCANREADY || receipt.state === CONST.IOU.RECEIPT_STATE.SCANNING; } -/** - * Returns the URL of a receipt given a filename - * @param {String} filename - * @returns {String} - */ -function getURL(filename) { - return `${CONFIG.EXPENSIFY.EXPENSIFY_URL}receipts/${filename}`; -} - -export {validateReceipt, getThumbnailAndImageURIs, getURL, isBeingScanned}; +export {validateReceipt, getThumbnailAndImageURIs, isBeingScanned}; diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index f7cdd3168f27..28f11d6ac261 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -151,7 +151,9 @@ function getCreated(transaction) { * @returns {Boolean} */ function isDistanceRequest(transaction) { - return transaction && transaction.type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && transaction.customUnit && transaction.customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE; + const type = lodashGet(transaction, 'comment.type'); + const customUnitName = lodashGet(transaction, 'comment.customUnit.name'); + return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE; } export {buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, getDescription, getAmount, getCurrency, getCreated, isDistanceRequest}; From 5451be69e4f66bfd1496bfec7e791a208eb2f348 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 16 Aug 2023 11:32:12 +0300 Subject: [PATCH 060/214] Fixing up lint errors --- src/Expensify.js | 6 +++--- src/libs/ReportUtils.js | 6 +++--- src/libs/actions/DemoActions.js | 15 ++++++++------- src/pages/DemoSetupPage.js | 5 ++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 3d6f7a861663..fd34f836d3ed 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -31,7 +31,7 @@ import SplashScreenHider from './components/SplashScreenHider'; import KeyboardShortcutsModal from './components/KeyboardShortcutsModal'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; import * as EmojiPickerAction from './libs/actions/EmojiPickerAction'; -import {runDemoByURL} from './libs/actions/App'; +import * as App from './libs/actions/App'; // This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection // eslint-disable-next-line no-unused-vars @@ -165,13 +165,13 @@ function Expensify(props) { // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report Linking.getInitialURL().then((url) => { - runDemoByURL(url); + App.runDemoByURL(url); Report.openReportFromDeepLink(url, isAuthenticated); }); // Open chat report from a deep link (only mobile native) Linking.addEventListener('url', (state) => { - runDemoByURL(state.url); + App.runDemoByURL(state.url); Report.openReportFromDeepLink(state.url, isAuthenticated); }); diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index bce20cbb43f2..4386e9dc09a6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3005,12 +3005,12 @@ function getPolicy(policyID) { * @returns {String|null} */ function getPolicyExpenseChatReportIDByOwner(policyOwner) { - const policy = _.find(allPolicies, (policy) => policy.owner === policyOwner); - if (!policy) { + const policyWithOwner = _.find(allPolicies, (policy) => policy.owner === policyOwner); + if (!policyWithOwner) { return null; } - return _.find(allReports, (report) => isPolicyExpenseChat(report) && report.policyID === policy.id); + return _.find(allReports, (report) => isPolicyExpenseChat(report) && report.policyID === policyWithOwner.id); } /* diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index 1029a92252d4..c8b734ad59e7 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -1,10 +1,11 @@ import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; +import _ from 'underscore'; import lodashGet from 'lodash/get'; import CONST from '../../CONST'; import * as API from '../API'; -import {buildOptimisticWorkspaceChats, buildOptimisticAddCommentReportAction, getPolicyExpenseChatReportIDByOwner} from '../ReportUtils'; -import {buildOptimisticCustomUnits, generatePolicyID} from './Policy'; +import * as ReportUtils from '../ReportUtils'; +import * as Policy from './Policy'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; import ONYXKEYS from '../../ONYXKEYS'; @@ -33,7 +34,7 @@ Onyx.connect({ function createSaastrDemoWorkspaceAndNavigate() { // Try to navigate to existing SaaStr expense chat if it exists in Onyx - const saastrWorkspaceChatReportID = getPolicyExpenseChatReportIDByOwner(CONST.EMAIL.SAASTR); + const saastrWorkspaceChatReportID = ReportUtils.getPolicyExpenseChatReportIDByOwner(CONST.EMAIL.SAASTR); if (saastrWorkspaceChatReportID) { // We must call goBack() to remove the /saastr route from history Navigation.goBack(); @@ -42,13 +43,13 @@ function createSaastrDemoWorkspaceAndNavigate() { } // Create workspace, owned and admin'd by SaaStr - const policyID = generatePolicyID(); + const policyID = Policy.generatePolicyID(); // Add domain name as prefix (only if domain is private) const domainNamePrefix = userIsFromPublicDomain ? `${Str.extractEmailDomain(sessionEmail)} ` : ''; const workspaceName = `${domainNamePrefix}SaaStr Workspace`; - const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); + const {customUnits, customUnitID, customUnitRateID} = Policy.buildOptimisticCustomUnits(); const { announceChatReportID, @@ -64,7 +65,7 @@ function createSaastrDemoWorkspaceAndNavigate() { expenseChatData, expenseReportActionData, expenseCreatedReportActionID, - } = buildOptimisticWorkspaceChats(policyID, workspaceName); + } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); // Add optimistic invite message comment const initialMessageText = ` @@ -76,7 +77,7 @@ function createSaastrDemoWorkspaceAndNavigate() { 3. Take a photo of the receipt, we'll automatically enter all the info. 4. Come say hi at the Expensify booth (#601) and let us know if you have any feedback! `; - const welcomeMessageReportAction = buildOptimisticAddCommentReportAction(initialMessageText); + const welcomeMessageReportAction = ReportUtils.buildOptimisticAddCommentReportAction(initialMessageText); welcomeMessageReportAction.reportAction.actorAccountID = CONST.ACCOUNT_ID.SAASTR; welcomeMessageReportAction.reportAction.person[0].text = 'SaaStr 2023'; welcomeMessageReportAction.reportAction.avatar = ''; diff --git a/src/pages/DemoSetupPage.js b/src/pages/DemoSetupPage.js index 735b91a6b1d8..5bf9a6a68f2f 100644 --- a/src/pages/DemoSetupPage.js +++ b/src/pages/DemoSetupPage.js @@ -1,4 +1,3 @@ -import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -6,7 +5,7 @@ import {useFocusEffect} from '@react-navigation/native'; import ONYXKEYS from '../ONYXKEYS'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import CONST from '../CONST'; -import {createSaastrDemoWorkspaceAndNavigate} from '../libs/actions/DemoActions'; +import * as DemoActions from '../libs/actions/DemoActions'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -26,7 +25,7 @@ function DemoSetupPage(props) { useFocusEffect(() => { // Depending on the route that the user hit to get here, run a specific demo flow if (props.route.name === CONST.DEMO_PAGES.SAASTR) { - createSaastrDemoWorkspaceAndNavigate(); + DemoActions.createSaastrDemoWorkspaceAndNavigate(); } }); From 887427ae55c7328a49e58523519891123a78c217 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Wed, 16 Aug 2023 22:46:21 +0800 Subject: [PATCH 061/214] replace setTimeout approach --- src/libs/focusWithDelay.js | 35 +++++++++++++++++ src/libs/focusWithDelay/focusWithDelay.js | 40 -------------------- src/libs/focusWithDelay/index.js | 7 ---- src/libs/focusWithDelay/index.native.js | 6 --- src/pages/home/report/ReportActionCompose.js | 33 ++-------------- 5 files changed, 39 insertions(+), 82 deletions(-) create mode 100644 src/libs/focusWithDelay.js delete mode 100644 src/libs/focusWithDelay/focusWithDelay.js delete mode 100644 src/libs/focusWithDelay/index.js delete mode 100644 src/libs/focusWithDelay/index.native.js diff --git a/src/libs/focusWithDelay.js b/src/libs/focusWithDelay.js new file mode 100644 index 000000000000..367cc2b92f9f --- /dev/null +++ b/src/libs/focusWithDelay.js @@ -0,0 +1,35 @@ +import {InteractionManager} from 'react-native'; +import ComposerFocusManager from './ComposerFocusManager'; + +/** + * Create a function that focuses a text input. + * @param {Object} textInput the text input to focus + * @returns {Function} a function that focuses the text input with a configurable delay + */ +function focusWithDelay(textInput) { + /** + * Focus the text input + * @param {Boolean} [shouldDelay=false] Impose delay before focusing the text input + */ + return (shouldDelay = false) => { + // There could be other animations running while we trigger manual focus. + // This prevents focus from making those animations janky. + InteractionManager.runAfterInteractions(() => { + if (!textInput) { + return; + } + if (!shouldDelay) { + textInput.focus(); + return; + } + ComposerFocusManager.isReadyToFocus().then(() => { + if (!textInput) { + return; + } + textInput.focus(); + }); + }); + }; +} + +export default focusWithDelay; diff --git a/src/libs/focusWithDelay/focusWithDelay.js b/src/libs/focusWithDelay/focusWithDelay.js deleted file mode 100644 index 143d5dd12430..000000000000 --- a/src/libs/focusWithDelay/focusWithDelay.js +++ /dev/null @@ -1,40 +0,0 @@ -import {InteractionManager} from 'react-native'; - -/** - * Creates a function that can be used to focus a text input - * @param {Boolean} disableDelay whether to force focus without a delay (on web and desktop) - * @returns {Function} a focusWithDelay function - */ -function focusWithDelay(disableDelay = false) { - /** - * Create a function that focuses a text input. - * @param {Object} textInput the text input to focus - * @returns {Function} a function that focuses the text input with a configurable delay - */ - return (textInput) => - /** - * Focus the text input - * @param {Boolean} [shouldDelay=false] Impose delay before focusing the text input - */ - (shouldDelay = false) => { - // There could be other animations running while we trigger manual focus. - // This prevents focus from making those animations janky. - InteractionManager.runAfterInteractions(() => { - if (!textInput) { - return; - } - - if (disableDelay || !shouldDelay) { - textInput.focus(); - } else { - // Keyboard is not opened after Emoji Picker is closed - // SetTimeout is used as a workaround - // https://github.com/react-native-modal/react-native-modal/issues/114 - // We carefully choose a delay. 100ms is found enough for keyboard to open. - setTimeout(() => textInput.focus(), 100); - } - }); - }; -} - -export default focusWithDelay; diff --git a/src/libs/focusWithDelay/index.js b/src/libs/focusWithDelay/index.js deleted file mode 100644 index faeb43147c5c..000000000000 --- a/src/libs/focusWithDelay/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import focusWithDelay from './focusWithDelay'; - -/** - * We pass true to disable the delay on the web because it doesn't require - * using the workaround (explained in the focusWithDelay.js file). - */ -export default focusWithDelay(true); diff --git a/src/libs/focusWithDelay/index.native.js b/src/libs/focusWithDelay/index.native.js deleted file mode 100644 index 27fb19fe1570..000000000000 --- a/src/libs/focusWithDelay/index.native.js +++ /dev/null @@ -1,6 +0,0 @@ -import focusWithDelay from './focusWithDelay'; - -/** - * We enable the delay on native to display the keyboard correctly - */ -export default focusWithDelay(false); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index bbe17caf1e35..d105bdef731b 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -1,11 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {View, InteractionManager, LayoutAnimation, NativeModules, findNodeHandle} from 'react-native'; +import {View, LayoutAnimation, NativeModules, findNodeHandle} from 'react-native'; import {runOnJS} from 'react-native-reanimated'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; +import focusWithDelay from '../../../libs/focusWithDelay'; import styles from '../../../styles/styles'; import themeColors from '../../../styles/themes/default'; import Composer from '../../../components/Composer'; @@ -59,7 +60,6 @@ import * as KeyDownListener from '../../../libs/KeyboardShortcut/KeyDownPressLis import * as EmojiPickerActions from '../../../libs/actions/EmojiPickerAction'; import withAnimatedRef from '../../../components/withAnimatedRef'; import updatePropsPaperWorklet from '../../../libs/updatePropsPaperWorklet'; -import ComposerFocusManager from '../../../libs/ComposerFocusManager'; const propTypes = { /** Beta features list */ @@ -178,7 +178,7 @@ class ReportActionCompose extends React.Component { this.submitForm = this.submitForm.bind(this); this.setIsFocused = this.setIsFocused.bind(this); this.setIsFullComposerAvailable = this.setIsFullComposerAvailable.bind(this); - this.focus = this.focus.bind(this); + this.focus = focusWithDelay(this.textInput).bind(this); this.replaceSelectionWithText = this.replaceSelectionWithText.bind(this); this.focusComposerOnKeyPress = this.focusComposerOnKeyPress.bind(this); this.checkComposerVisibility = this.checkComposerVisibility.bind(this); @@ -393,6 +393,7 @@ class ReportActionCompose extends React.Component { if (_.isFunction(this.props.animatedRef)) { this.props.animatedRef(el); } + this.focus = focusWithDelay(this.textInput).bind(this); } /** @@ -753,32 +754,6 @@ class ReportActionCompose extends React.Component { this.replaceSelectionWithText(e.key, false); } - /** - * Focus the composer text input - * @param {Boolean} [shouldelay=false] Impose delay before focusing the composer - * @memberof ReportActionCompose - */ - focus(shouldelay = false) { - // There could be other animations running while we trigger manual focus. - // This prevents focus from making those animations janky. - InteractionManager.runAfterInteractions(() => { - if (!this.textInput) { - return; - } - - if (!shouldelay) { - this.textInput.focus(); - return; - } - ComposerFocusManager.isReadyToFocus().then(() => { - if (!this.textInput) { - return; - } - this.textInput.focus(); - }); - }); - } - /** * Save our report comment in Onyx. We debounce this method in the constructor so that it's not called too often * to update Onyx and re-render this component. From 1817b399612755a03dca49190dca5eae2b578f6a Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 16 Aug 2023 11:45:52 -0600 Subject: [PATCH 062/214] Fix whitespace --- src/components/ReportActionItem/MoneyRequestView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index c65302483342..76f53965633b 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -92,7 +92,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, polic if (ReportActionsUtils.isDeletedAction(parentReportAction)) { return null; } - + const transaction = TransactionUtils.getTransaction(parentReportAction.originalMessage.IOUTransactionID); const hasReceipt = TransactionUtils.hasReceipt(transaction); let receiptURIs; From c40ebaeb83fddb9d2b6c644d7114cd061c86b0db Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 16 Aug 2023 11:59:41 -0600 Subject: [PATCH 063/214] Fix bad merge --- src/libs/TransactionUtils.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 694b0d2fc891..b00948040892 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -1,10 +1,24 @@ import {format} from 'date-fns'; import lodashGet from 'lodash/get'; +import Onyx from 'react-native-onyx'; import _ from 'underscore'; import CONST from '../CONST'; +import ONYXKEYS from '../ONYXKEYS'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; +let allTransactions = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (val) => { + if (!val) { + return; + } + allTransactions = val; + }, +}); + /** * Optimistically generate a transaction. * @@ -87,6 +101,16 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep return updatedTransaction; } +/** + * Retrieve the particular transaction object given its ID. + * + * @param {String} transactionID + * @returns {Object} + */ +function getTransaction(transactionID) { + return lodashGet(allTransactions, `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {}); +} + /** * Return the comment field (referred to as description in the App) from the transaction. * The comment does not have its modifiedComment counterpart. @@ -181,11 +205,11 @@ export { buildOptimisticTransaction, hasReceipt, getUpdatedTransaction, - getTransaction, getDescription, getAmount, getCurrency, getCreated, getReportPreviewTransactionsWithReceipts, + getTransaction, isDistanceRequest, }; From 49d669c23129e67bb97b3b6fd6c6605217e5131c Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 16 Aug 2023 15:54:56 -0600 Subject: [PATCH 064/214] Remove duplicate receipt image --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index cb22f37c5d54..d103c5152a20 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -242,8 +242,6 @@ function MoneyRequestPreview(props) { hoverStyle={isScanning ? styles.moneyRequestPreviewBoxHover : undefined} /> )} - - {isDistanceRequest && } From a021e0f92a780d4e3914f42f008ba0365ceb5ec3 Mon Sep 17 00:00:00 2001 From: ntdiary <2471314@gmail.com> Date: Thu, 17 Aug 2023 11:50:26 +0800 Subject: [PATCH 065/214] preserve keyboard state --- src/pages/home/report/ReportActionCompose.js | 42 ++++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index d105bdef731b..7ead07cbdb49 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -196,6 +196,7 @@ class ReportActionCompose extends React.Component { this.updateNumberOfLines = this.updateNumberOfLines.bind(this); this.showPopoverMenu = this.showPopoverMenu.bind(this); this.debouncedUpdateFrequentlyUsedEmojis = _.debounce(this.debouncedUpdateFrequentlyUsedEmojis.bind(this), 1000, false); + this.restoreKeyboardState = this.restoreKeyboardState.bind(this); this.comment = props.comment; this.insertedEmojis = []; @@ -215,6 +216,9 @@ class ReportActionCompose extends React.Component { this.shouldBlockEmojiCalc = false; this.shouldBlockMentionCalc = false; + this.willOpenNextModal = false; + this.isKeyboardVisibleWhenShowingModal = false; + // For mobile Safari, updating the selection prop on an unfocused input will cause it to automatically gain focus // and subsequent programmatic focus shifts (e.g., modal focus trap) to show the blue frame (:focus-visible style), // so we need to ensure that it is only updated after focus. @@ -223,8 +227,6 @@ class ReportActionCompose extends React.Component { this.unsubscribeNavigationBlur = () => null; this.unsubscribeNavigationFocus = () => null; - this.shouldFocusAfterClosingModal = true; - this.state = { isFocused: this.shouldFocusInputOnScreenFocus && !this.props.modal.isVisible && !this.props.modal.willAlertModalBecomeVisible && this.props.shouldShowComposeInput, isFullComposerAvailable: props.isComposerFullSize, @@ -274,18 +276,12 @@ class ReportActionCompose extends React.Component { componentDidUpdate(prevProps) { if (this.props.modal.isVisible && !prevProps.modal.isVisible) { - this.shouldFocusAfterClosingModal = true; + this.willOpenNextModal = false; } // We want to focus or refocus the input when a modal has been closed or the underlying screen is refocused. // We avoid doing this on native platforms since the software keyboard popping // open creates a jarring and broken UX. - if ( - this.willBlurTextInputOnTapOutside && - this.shouldFocusAfterClosingModal && - !this.props.modal.isVisible && - this.props.isFocused && - (prevProps.modal.isVisible || !prevProps.isFocused) - ) { + if (this.willBlurTextInputOnTapOutside && !this.willOpenNextModal && !this.props.modal.isVisible && this.props.isFocused && (prevProps.modal.isVisible || !prevProps.isFocused)) { this.focus(); } @@ -961,6 +957,14 @@ class ReportActionCompose extends React.Component { return true; } + restoreKeyboardState() { + if (!this.isKeyboardVisibleWhenShowingModal) { + return; + } + this.focus(true); + this.isKeyboardVisibleWhenShowingModal = false; + } + render() { const reportParticipants = _.without(lodashGet(this.props.report, 'participantAccountIDs', []), this.props.currentUserPersonalDetails.accountID); const participantsWithoutExpensifyAccountIDs = _.difference(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS); @@ -1023,7 +1027,7 @@ class ReportActionCompose extends React.Component { this.shouldBlockEmojiCalc = false; this.shouldBlockMentionCalc = false; this.setState({isAttachmentPreviewActive: false}); - this.focus(true); + this.restoreKeyboardState(); }} > {({displayFileInModal}) => ( @@ -1037,12 +1041,10 @@ class ReportActionCompose extends React.Component { this.shouldBlockEmojiCalc = true; this.shouldBlockMentionCalc = true; } - this.shouldFocusAfterClosingModal = false; + this.willOpenNextModal = true; openPicker({ onPicked: displayFileInModal, - onCanceled: () => { - this.focus(true); - }, + onCanceled: this.restoreKeyboardState, }); }; const menuItems = [ @@ -1111,6 +1113,9 @@ class ReportActionCompose extends React.Component { ref={this.actionButtonRef} onPress={(e) => { e.preventDefault(); + if (!this.willBlurTextInputOnTapOutside) { + this.isKeyboardVisibleWhenShowingModal = this.textInput.isFocused(); + } this.textInput.blur(); // Drop focus to avoid blue focus ring. @@ -1131,7 +1136,7 @@ class ReportActionCompose extends React.Component { isVisible={this.state.isMenuVisible} onClose={() => { this.setMenuVisibility(false); - this.focus(true); + this.restoreKeyboardState(); }} onItemSelected={(item, index) => { this.setMenuVisibility(false); @@ -1167,9 +1172,12 @@ class ReportActionCompose extends React.Component { style={[styles.textInputCompose, this.props.isComposerFullSize ? styles.textInputFullCompose : styles.flex4]} maxLines={maxComposerLines} onFocus={() => this.setIsFocused(true)} - onBlur={() => { + onBlur={(e) => { this.setIsFocused(false); this.resetSuggestions(); + if (e.relatedTarget && e.relatedTarget === this.actionButtonRef.current) { + this.isKeyboardVisibleWhenShowingModal = true; + } }} onClick={() => { this.shouldBlockEmojiCalc = false; From 1b56731b29bb720072005c19fcb4151cc81947da Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 17 Aug 2023 15:30:12 +0300 Subject: [PATCH 066/214] Remove admin rooms, send welcome report action id --- src/libs/actions/DemoActions.js | 55 ++------------------------------- 1 file changed, 3 insertions(+), 52 deletions(-) diff --git a/src/libs/actions/DemoActions.js b/src/libs/actions/DemoActions.js index c8b734ad59e7..094d74402f1a 100644 --- a/src/libs/actions/DemoActions.js +++ b/src/libs/actions/DemoActions.js @@ -56,11 +56,6 @@ function createSaastrDemoWorkspaceAndNavigate() { announceChatData, announceReportActionData, announceCreatedReportActionID, - // TODO: maybe comment these admin report data out b/c user shouldn't an admin to start with, right? :think: - adminsChatReportID, - adminsChatData, - adminsReportActionData, - adminsCreatedReportActionID, expenseChatReportID, expenseChatData, expenseReportActionData, @@ -83,7 +78,8 @@ function createSaastrDemoWorkspaceAndNavigate() { welcomeMessageReportAction.reportAction.avatar = ''; // Update policy expense chat report actions with welcome message from saastr - expenseReportActionData[welcomeMessageReportAction.reportAction.reportActionID] = welcomeMessageReportAction.reportAction; + const expenseChatWelcomeReportActionID = welcomeMessageReportAction.reportAction.reportActionID; + expenseReportActionData[expenseChatWelcomeReportActionID] = welcomeMessageReportAction.reportAction; // Update report with info about last message sent const currentTime = DateUtils.getDBTime(); @@ -97,12 +93,11 @@ function createSaastrDemoWorkspaceAndNavigate() { { policyID, announceChatReportID, - adminsChatReportID, expenseChatReportID, policyName: workspaceName, announceCreatedReportActionID, - adminsCreatedReportActionID, expenseCreatedReportActionID, + expenseChatWelcomeReportActionID, customUnitID, customUnitRateID, }, @@ -151,21 +146,6 @@ function createSaastrDemoWorkspaceAndNavigate() { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: announceReportActionData, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...adminsChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, - }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, @@ -207,25 +187,6 @@ function createSaastrDemoWorkspaceAndNavigate() { }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - [_.keys(adminsChatData)[0]]: { - pendingAction: null, - }, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, @@ -262,16 +223,6 @@ function createSaastrDemoWorkspaceAndNavigate() { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: null, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: null, - }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, From 22bd4b4d41d8752d706f7251beb65bc1c080aa8d Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 17 Aug 2023 17:16:29 +0200 Subject: [PATCH 067/214] add strings --- src/languages/en.js | 1 + src/languages/es.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/languages/en.js b/src/languages/en.js index 4ce07f7e00ca..e3878fdd8517 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -151,6 +151,7 @@ export default { edit: 'Edit', showMore: 'Show more', merchant: 'Merchant', + category: 'Category', }, anonymousReportFooter: { logoTagline: 'Join in on the discussion.', diff --git a/src/languages/es.js b/src/languages/es.js index 7cee163470f3..c3cab2702d6d 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -150,6 +150,7 @@ export default { edit: 'Editar', showMore: 'Mostrar mรกs', merchant: 'Comerciante', + category: 'Categorรญa', }, anonymousReportFooter: { logoTagline: 'รšnete a la discussion.', From ab6a7ec2a7a2cbf7f182989b82b8f9fcae932309 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 17 Aug 2023 17:17:06 +0200 Subject: [PATCH 068/214] add MoneyRequestCategoryPage --- src/ROUTES.js | 2 + .../AppNavigator/ModalStackNavigators.js | 7 +++ src/libs/Navigation/linkingConfig.js | 1 + src/pages/iou/MoneyRequestCategoryPage.js | 49 +++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 src/pages/iou/MoneyRequestCategoryPage.js diff --git a/src/ROUTES.js b/src/ROUTES.js index 90d08e5b13c8..bcee5d488405 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -92,6 +92,7 @@ export default { MONEY_REQUEST_CONFIRMATION: ':iouType/new/confirmation/:reportID?', MONEY_REQUEST_CURRENCY: ':iouType/new/currency/:reportID?', MONEY_REQUEST_DESCRIPTION: ':iouType/new/description/:reportID?', + MONEY_REQUEST_CATEGORY: ':iouType/new/category/:reportID?', MONEY_REQUEST_MANUAL_TAB: ':iouType/new/:reportID?/manual', MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan', MONEY_REQUEST_DISTANCE_TAB: ':iouType/new/:reportID?/distance', @@ -104,6 +105,7 @@ export default { getMoneyRequestConfirmationRoute: (iouType, reportID = '') => `${iouType}/new/confirmation/${reportID}`, getMoneyRequestCurrencyRoute: (iouType, reportID = '', currency, backTo) => `${iouType}/new/currency/${reportID}?currency=${currency}&backTo=${backTo}`, getMoneyRequestDescriptionRoute: (iouType, reportID = '') => `${iouType}/new/description/${reportID}`, + getMoneyRequestCategoryRoute: (iouType, reportID = '') => `${iouType}/new/category/${reportID}`, SPLIT_BILL_DETAILS: `r/:reportID/split/:reportActionID`, getSplitBillDetailsRoute: (reportID, reportActionID) => `r/${reportID}/split/${reportActionID}`, getNewTaskRoute: (reportID) => `${NEW_TASK}/${reportID}`, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 8a489afb035e..9fcc89dd2950 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -76,6 +76,13 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator([ }, name: 'Money_Request_Description', }, + { + getComponent: () => { + const MoneyRequestCategoryPage = require('../../../pages/iou/MoneyRequestCategoryPage').default; + return MoneyRequestCategoryPage; + }, + name: 'Money_Request_Category', + }, { getComponent: () => { const AddPersonalBankAccountPage = require('../../../pages/AddPersonalBankAccountPage').default; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index dcc4f77fde73..4814955325df 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -321,6 +321,7 @@ export default { Money_Request_Confirmation: ROUTES.MONEY_REQUEST_CONFIRMATION, Money_Request_Currency: ROUTES.MONEY_REQUEST_CURRENCY, Money_Request_Description: ROUTES.MONEY_REQUEST_DESCRIPTION, + Money_Request_Category: ROUTES.MONEY_REQUEST_CATEGORY, IOU_Send_Enable_Payments: ROUTES.IOU_SEND_ENABLE_PAYMENTS, IOU_Send_Add_Bank_Account: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, IOU_Send_Add_Debit_Card: ROUTES.IOU_SEND_ADD_DEBIT_CARD, diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js new file mode 100644 index 000000000000..b69d9c683e06 --- /dev/null +++ b/src/pages/iou/MoneyRequestCategoryPage.js @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; +import ROUTES from '../../ROUTES'; +import Navigation from '../../libs/Navigation/Navigation'; +import useLocalize from '../../hooks/useLocalize'; +import ScreenWrapper from '../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../components/HeaderWithBackButton'; + +const propTypes = { + route: PropTypes.shape({ + /** Each parameter passed via the URL */ + params: PropTypes.shape({ + /** TODO: Comment */ + iouType: PropTypes.string, + + /** TODO: Comment */ + reportID: PropTypes.string, + }), + }).isRequired, +}; + +function MoneyRequestCategoryPage({route}) { + const {translate} = useLocalize(); + + const navigateBack = () => { + const iouType = lodashGet(route, 'params.iouType', ''); + const reportID = lodashGet(route, 'params.reportID', ''); + + Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID)); + }; + + return ( + + + + ); +} + +MoneyRequestCategoryPage.displayName = 'MoneyRequestCategoryPage'; +MoneyRequestCategoryPage.propTypes = propTypes; + +export default MoneyRequestCategoryPage; From f95d8bb421929b6d210f497563753a11f4f57e80 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 17 Aug 2023 17:17:37 +0200 Subject: [PATCH 069/214] add category to MoneyRequestConfirmationList --- .../MoneyRequestConfirmationList.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 2cc458d0e4ad..88e78fa820e5 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -63,6 +63,9 @@ const propTypes = { /** IOU merchant */ iouMerchant: PropTypes.string, + /** TODO: Comment */ + iouCategory: PropTypes.string, + /** Selected participants from MoneyRequestModal with login / accountID */ selectedParticipants: PropTypes.arrayOf(optionPropTypes).isRequired, @@ -96,6 +99,14 @@ const propTypes = { /** File source of the receipt */ receiptSource: PropTypes.string, + + /** TODO: Comment */ + policyCategories: PropTypes.objectOf( + PropTypes.shape({ + enabled: PropTypes.bool.isRequired, + name: PropTypes.string.isRequired, + }), + ), }; const defaultProps = { @@ -103,6 +114,7 @@ const defaultProps = { onSendMoney: () => {}, onSelectParticipant: () => {}, iouType: CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, + iouCategory: '', payeePersonalDetails: null, canModifyParticipants: false, isReadOnly: false, @@ -115,6 +127,7 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, receiptPath: '', receiptSource: '', + policyCategories: null, }; function MoneyRequestConfirmationList(props) { @@ -419,6 +432,16 @@ function MoneyRequestConfirmationList(props) { // Note: This component is disabled until this field is editable in next PR disabled /> + {props.policyCategories !== null && ( + Navigation.navigate(ROUTES.getMoneyRequestCategoryRoute(props.iouType, props.reportID))} + style={[styles.moneyRequestMenuItem, styles.mb2]} + disabled={didConfirm || props.isReadOnly} + /> + )} )} @@ -434,5 +457,8 @@ export default compose( session: { key: ONYXKEYS.SESSION, }, + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, }), )(MoneyRequestConfirmationList); From d20ba5e9f036d9b3f58fbdfee137f0e01ae6dfea Mon Sep 17 00:00:00 2001 From: Georgia Monahan Date: Thu, 17 Aug 2023 11:30:08 -0500 Subject: [PATCH 070/214] Add Mobile Banner Component, Styled (not functional) --- assets/images/expensify-app-icon.svg | 18 +++++ src/Expensify.js | 2 + src/components/MobileBanner/index.js | 108 +++++++++++++++++++++++++++ src/styles/styles.js | 5 ++ src/styles/utilities/spacing.js | 16 ++++ src/styles/variables.js | 1 + 6 files changed, 150 insertions(+) create mode 100644 assets/images/expensify-app-icon.svg create mode 100644 src/components/MobileBanner/index.js diff --git a/assets/images/expensify-app-icon.svg b/assets/images/expensify-app-icon.svg new file mode 100644 index 000000000000..a0adfe7dd952 --- /dev/null +++ b/assets/images/expensify-app-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/src/Expensify.js b/src/Expensify.js index c85c2862e96e..ecdb1a809152 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -31,6 +31,7 @@ import SplashScreenHider from './components/SplashScreenHider'; import KeyboardShortcutsModal from './components/KeyboardShortcutsModal'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; import * as EmojiPickerAction from './libs/actions/EmojiPickerAction'; +import MobileBanner from './components/MobileBanner'; // This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection // eslint-disable-next-line no-unused-vars @@ -191,6 +192,7 @@ function Expensify(props) { {/* We include the modal for showing a new update at the top level so the option is always present. */} + {true ? : null} {props.updateAvailable ? : null} {props.screenShareRequest ? ( + + + + + + {"Download the app"} + + + {"Keep the conversation going in New Expensify."} + + + + +