Message Request UI Improvements (#9938)
@@ -2419,6 +2419,22 @@
|
||||
"messageformat": "Message",
|
||||
"description": "In conversation details, label for button to switch to the conversation view in order to draft a message in that converation"
|
||||
},
|
||||
"icu:ConversationDetails--help-section": {
|
||||
"messageformat": "Help",
|
||||
"description": "Title of the help section in the conversation details screen"
|
||||
},
|
||||
"icu:ConversationDetails--support-center": {
|
||||
"messageformat": "Support Center",
|
||||
"description": "Label for the support center link in conversation details"
|
||||
},
|
||||
"icu:ConversationDetails--contact-us": {
|
||||
"messageformat": "Contact us",
|
||||
"description": "Label for the contact us link in conversation details"
|
||||
},
|
||||
"icu:ConversationDetails--donate": {
|
||||
"messageformat": "Donate to Signal",
|
||||
"description": "Label for the donation link in conversation details"
|
||||
},
|
||||
"icu:SafetyNumberNotification__viewSafetyNumber": {
|
||||
"messageformat": "View Safety Number",
|
||||
"description": "In conversation, safety number change notification, label for button to view safety number, opens safety number modal"
|
||||
@@ -3635,6 +3651,14 @@
|
||||
"messageformat": "Be careful when accepting message requests from people you don’t know. Watch out for:",
|
||||
"description": "Description of the safety tips modal"
|
||||
},
|
||||
"icu:SafetyTipsModal__TipTitle--Fake": {
|
||||
"messageformat": "Fake names and accounts",
|
||||
"description": "Title of the fake name safety tip"
|
||||
},
|
||||
"icu:SafetyTipsModal__TipDescription--Fake": {
|
||||
"messageformat": "Signal will never contact you for your registration code or PIN. Be cautious of requests that impersonate others. Profile names are chosen by their account holder and aren't verified.",
|
||||
"description": "Description of the fake name safety tip"
|
||||
},
|
||||
"icu:SafetyTipsModal__TipTitle--Crypto": {
|
||||
"messageformat": "Crypto or money scams",
|
||||
"description": "Title of the crypto safety tip"
|
||||
@@ -3811,6 +3835,14 @@
|
||||
"messageformat": "Accept",
|
||||
"description": "Shown as a button to let the user accept a message request"
|
||||
},
|
||||
"icu:MessageRequests--accept-confirm-title": {
|
||||
"messageformat": "Accept Request?",
|
||||
"description": "Title of confirmation dialog shown before accepting a message request"
|
||||
},
|
||||
"icu:MessageRequests--accept-confirm-body": {
|
||||
"messageformat": "Review requests carefully. Profile names are chosen by their account owner and aren't verified.",
|
||||
"description": "Body text of confirmation dialog shown before accepting a message request"
|
||||
},
|
||||
"icu:MessageRequests--continue": {
|
||||
"messageformat": "Continue",
|
||||
"description": "Shown as a button to share your profile, necessary to continue messaging in a conversation"
|
||||
@@ -3827,6 +3859,26 @@
|
||||
"messageformat": "{count, plural, one {# member} other {# members}}",
|
||||
"description": "Specifies the number of members in a group conversation"
|
||||
},
|
||||
"icu:ConversationHero--review-carefully": {
|
||||
"messageformat": "Review carefully",
|
||||
"description": "Label shown in conversation hero to advise users to review the conversation carefully"
|
||||
},
|
||||
"icu:ConversationHero--group-names": {
|
||||
"messageformat": "<clickable>Group names</clickable> are not verified",
|
||||
"description": "Label for group names in the name verification warning in conversation hero"
|
||||
},
|
||||
"icu:ConversationHero--profile-names": {
|
||||
"messageformat": "<clickable>Profile names</clickable> are not verified",
|
||||
"description": "Label for profile names in the name verification warning in conversation hero"
|
||||
},
|
||||
"icu:ConversationHero--signal-official-chat": {
|
||||
"messageformat": "This is the official and only chat from Signal",
|
||||
"description": "Text indicating that this is the official Signal conversation"
|
||||
},
|
||||
"icu:ConversationHero--release-notes": {
|
||||
"messageformat": "Keep up to date with news and release notes.",
|
||||
"description": "Text explaining the purpose of the Signal official conversation"
|
||||
},
|
||||
"icu:member-of-1-group": {
|
||||
"messageformat": "Member of {group}",
|
||||
"description": "Shown in the conversation hero to indicate this user is a member of a mutual group"
|
||||
@@ -5823,10 +5875,6 @@
|
||||
"messageformat": "Use this link to join a Signal call: {url}",
|
||||
"description": "Draft message text for sharing a call link"
|
||||
},
|
||||
"icu:MessageRequestWarning__learn-more": {
|
||||
"messageformat": "Learn more",
|
||||
"description": "Shown on the message request warning. Clicking this button will open a dialog with more information"
|
||||
},
|
||||
"icu:MessageRequestWarning__safety-tips": {
|
||||
"messageformat": "Safety Tips",
|
||||
"description": "Shown on the message request warning. Clicking this button will open a dialog with safety tips"
|
||||
@@ -8077,6 +8125,38 @@
|
||||
"messageformat": "To change this setting, set “Who can see my number” to “Nobody”.",
|
||||
"description": "A toast displayed when user clicks disabled option in settings window"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__description--direct": {
|
||||
"messageformat": "Profile names on Signal are chosen by their account holder:",
|
||||
"description": "Description of how profile names work in the profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item1--direct": {
|
||||
"messageformat": "Profile names aren't verified",
|
||||
"description": "First list item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item2--direct": {
|
||||
"messageformat": "Be cautious of accounts that impersonate others",
|
||||
"description": "Second list item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item3--direct": {
|
||||
"messageformat": "Don't share personal information with people you don't know",
|
||||
"description": "Third list item in profile name warning modal for direct conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__description--group": {
|
||||
"messageformat": "Group names are chosen by members of the group.",
|
||||
"description": "Description of how group names work in the profile name warning modal for group conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item1--group": {
|
||||
"messageformat": "Be cautious of groups that impersonate organizations and businesses",
|
||||
"description": "First list item in profile name warning modal for group conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item2--group": {
|
||||
"messageformat": "Profile names of members in groups are not verified",
|
||||
"description": "Second list item in profile name warning modal for group conversations"
|
||||
},
|
||||
"icu:ProfileNameWarningModal__list--item3--group": {
|
||||
"messageformat": "Don't share personal information with people you don't know",
|
||||
"description": "Third list item in profile name warning modal for group conversations"
|
||||
},
|
||||
"icu:WhatsNew__modal-title": {
|
||||
"messageformat": "What's New",
|
||||
"description": "Title for the whats new modal"
|
||||
|
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.274 1.709c.772-1.32 2.68-1.32 3.452 0l5.384 9.197c.78 1.333-.181 3.01-1.726 3.01H2.616c-1.545 0-2.506-1.677-1.726-3.01L6.274 1.71ZM8 4.125a.957.957 0 0 1 .953 1.043l-.319 3.503a.637.637 0 0 1-1.268 0l-.319-3.503A.957.957 0 0 1 8 4.125ZM8 10a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z" fill="#000"/></svg>
|
After Width: | Height: | Size: 417 B |
8
images/icons/v3/group/group-questionmark-compact.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.97709 8.9949C6.37045 8.74041 5.69775 8.6 4.99998 8.6C2.5161 8.6 0.349976 10.3792 0.349976 12.75C0.349976 13.2841 0.794682 13.65 1.26029 13.65H5.33077C5.27778 13.3604 5.24998 13.0601 5.24998 12.7501C5.24998 12.6148 5.25527 12.4814 5.26563 12.35H1.68382C1.90938 11.0216 3.23994 9.9 4.99998 9.9C5.37503 9.9 5.73058 9.95093 6.06009 10.0439C6.31688 9.65538 6.62656 9.30394 6.97709 8.9949Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.68382 12.35H14.3161C14.0906 11.0216 12.76 9.9 11 9.9C9.23994 9.9 7.90938 11.0216 7.68382 12.35ZM6.34998 12.75C6.34998 10.3792 8.51609 8.6 11 8.6C13.4839 8.6 15.65 10.3792 15.65 12.75C15.65 13.2841 15.2053 13.65 14.7397 13.65H7.26029C6.79468 13.65 6.34998 13.2841 6.34998 12.75Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 3.65C10.4806 3.65 9.89998 4.14904 9.89998 5.05859C9.89998 5.50517 10.0502 5.90977 10.2727 6.19164C10.4952 6.47343 10.7582 6.6 11 6.6C11.2417 6.6 11.5048 6.47343 11.7272 6.19164C11.9498 5.90977 12.1 5.50517 12.1 5.05859C12.1 4.14904 11.5194 3.65 11 3.65ZM8.59998 5.05859C8.59998 3.62093 9.5864 2.35 11 2.35C12.4135 2.35 13.4 3.62093 13.4 5.05859C13.4 5.78562 13.1584 6.47673 12.7476 6.99716C12.3367 7.51767 11.7247 7.9 11 7.9C10.2752 7.9 9.66327 7.51767 9.25236 6.99716C8.8415 6.47673 8.59998 5.78562 8.59998 5.05859Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99998 3.65C4.48055 3.65 3.89998 4.14904 3.89998 5.05859C3.89998 5.50517 4.0502 5.90977 4.27272 6.19164C4.49518 6.47343 4.75824 6.6 4.99998 6.6C5.24172 6.6 5.50477 6.47343 5.72723 6.19164C5.94975 5.90977 6.09998 5.50517 6.09998 5.05859C6.09998 4.14904 5.5194 3.65 4.99998 3.65ZM2.59998 5.05859C2.59998 3.62093 3.5864 2.35 4.99998 2.35C6.41355 2.35 7.39998 3.62093 7.39998 5.05859C7.39998 5.78562 7.15845 6.47673 6.74759 6.99716C6.33668 7.51767 5.72473 7.9 4.99998 7.9C4.27522 7.9 3.66327 7.51767 3.25236 6.99716C2.8415 6.47673 2.59998 5.78562 2.59998 5.05859Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9953 3.98568C17.4081 3.71538 17.924 3.6 18.5 3.6C19.1867 3.6 19.7834 3.77836 20.2169 4.12822C20.6583 4.48435 20.9 4.99502 20.9 5.58C20.9 6.05249 20.7642 6.41287 20.5533 6.70699C20.3667 6.96716 20.1246 7.16734 19.9252 7.3323C19.9119 7.34333 19.8987 7.3542 19.8858 7.36492C19.6656 7.54761 19.4925 7.6988 19.3662 7.88968C19.248 8.06841 19.1609 8.29902 19.1609 8.64912V8.73913C19.1609 9.10412 18.865 9.4 18.5 9.4C18.135 9.4 17.8391 9.10412 17.8391 8.73913V8.64912C17.8391 8.12483 17.9659 7.71609 18.1693 7.38236C18.368 7.05617 18.6278 6.82253 18.8525 6.63519C18.8882 6.60549 18.9224 6.57708 18.9554 6.54978C19.3755 6.20161 19.5782 6.03361 19.5782 5.66667C19.5782 5.33553 19.4634 5.12445 19.3011 4.98892C19.1289 4.84513 18.8595 4.75088 18.5 4.75088C18.1491 4.75088 17.9156 4.82823 17.7629 4.93683C17.6155 5.04169 17.5065 5.20163 17.4496 5.44448C17.3844 5.72298 17.1375 5.98 16.7918 5.98C16.4134 5.98 16.0555 5.63298 16.1657 5.19555C16.297 4.67464 16.5742 4.26141 16.9953 3.98568Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.75 11C17.75 10.5858 18.0858 10.25 18.5 10.25C18.9142 10.25 19.25 10.5858 19.25 11C19.25 11.4142 18.9142 11.75 18.5 11.75C18.0858 11.75 17.75 11.4142 17.75 11Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
1
images/icons/v3/help/help-light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.825 6.89c.55-.356 1.248-.515 2.05-.515.954 0 1.762.245 2.341.708.587.47.909 1.141.909 1.918 0 .633-.182 1.107-.462 1.494-.252.35-.582.62-.872.857l-.053.044c-.315.26-.586.491-.785.79-.192.288-.328.654-.328 1.189v.125a.75.75 0 0 1-1.5 0v-.125c0-.714.174-1.261.446-1.705.268-.436.62-.752.94-1.016l.164-.135c.583-.477.95-.777.95-1.394 0-.522-.185-.88-.469-1.114-.293-.243-.731-.386-1.281-.386-.533 0-.913.116-1.176.301-.258.182-.438.454-.528.837-.077.323-.362.614-.751.614-.432 0-.83-.395-.705-.88.179-.7.551-1.244 1.11-1.606Zm4.432 1.46a2.441 2.441 0 0 1 .118.652c0-.236-.04-.454-.118-.652Z" fill="#000"/><path d="M10.75 16.625a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z" fill="#000"/><path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 12C1.25 6.063 6.063 1.25 12 1.25S22.75 6.063 22.75 12 17.937 22.75 12 22.75 1.25 17.937 1.25 12ZM12 2.75a9.25 9.25 0 1 0 0 18.5 9.25 9.25 0 0 0 0-18.5Z" fill="#000"/></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
images/icons/v3/invite/invite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.053 3.02c-.674 0-1.224 0-1.671.037-.462.038-.877.118-1.265.316a3.23 3.23 0 0 0-1.41 1.411c-.198.387-.279.803-.316 1.265-.037.446-.037.996-.037 1.67v4.562c0 .674 0 1.224.037 1.67.037.462.118.878.315 1.265a3.23 3.23 0 0 0 1.411 1.411c.388.198.803.278 1.265.316.447.036.997.036 1.67.036h7.895c.674 0 1.224 0 1.671-.036.462-.038.877-.118 1.265-.316a3.23 3.23 0 0 0 1.41-1.41c.198-.388.279-.804.316-1.266.037-.446.037-.996.037-1.67V7.719c0-.674 0-1.224-.037-1.67-.037-.462-.118-.878-.315-1.265a3.23 3.23 0 0 0-1.411-1.411c-.388-.198-.803-.278-1.265-.316-.447-.036-.997-.036-1.67-.036H6.052ZM3.779 4.673c.148-.075.35-.13.722-.161.38-.031.87-.032 1.582-.032h7.834c.712 0 1.202 0 1.582.032.372.03.574.086.722.161.333.17.604.44.773.774a.86.86 0 0 1 .053.122L11.05 9.987a1.77 1.77 0 0 1-2.1 0L2.953 5.568a1.04 1.04 0 0 1 .052-.122c.17-.333.441-.604.774-.774Zm-.966 2.605v4.973c0 .712 0 1.202.031 1.583.03.371.086.573.161.721.17.333.441.604.774.774.148.075.35.13.722.161.38.031.87.032 1.582.032h7.834c.712 0 1.202 0 1.582-.032.372-.03.574-.086.722-.161.333-.17.604-.44.773-.774.076-.148.131-.35.162-.721.03-.38.032-.87.032-1.583v-4.5l-.001-.473-5.272 3.884a3.23 3.23 0 0 1-3.83 0L2.812 7.277Z" fill="#000"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
6
images/icons/v3/person/person-questionmark-compact.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99998 3.39998C4.40487 3.39998 3.77498 3.95875 3.77498 4.92966C3.77498 5.95712 4.45204 6.59998 4.99998 6.59998C5.54791 6.59998 6.22498 5.95712 6.22498 4.92966C6.22498 3.95875 5.59508 3.39998 4.99998 3.39998ZM2.47498 4.92966C2.47498 3.4153 3.52401 2.09998 4.99998 2.09998C6.47594 2.09998 7.52498 3.4153 7.52498 4.92966C7.52498 6.38749 6.52311 7.89998 4.99998 7.89998C3.47684 7.89998 2.47498 6.38749 2.47498 4.92966Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.43699 12.35H8.56296C8.32128 11.0449 6.91792 9.89998 4.99998 9.89998C3.08203 9.89998 1.67867 11.0449 1.43699 12.35ZM0.0999756 12.75C0.0999756 10.3456 2.41754 8.59998 4.99998 8.59998C7.58241 8.59998 9.89997 10.3456 9.89997 12.75C9.89997 13.2864 9.4526 13.65 8.989 13.65H1.01095C0.547352 13.65 0.0999756 13.2864 0.0999756 12.75Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9953 3.98566C11.4081 3.71536 11.924 3.59998 12.5 3.59998C13.1867 3.59998 13.7834 3.77834 14.2169 4.12819C14.6583 4.48433 14.9 4.995 14.9 5.57998C14.9 6.05247 14.7642 6.41285 14.5533 6.70697C14.3667 6.96713 14.1246 7.16732 13.9252 7.33228C13.9119 7.3433 13.8987 7.35417 13.8858 7.36489C13.6656 7.54758 13.4925 7.69878 13.3662 7.88966C13.248 8.06839 13.1608 8.29899 13.1608 8.6491V8.73911C13.1608 9.10409 12.865 9.39998 12.5 9.39998C12.135 9.39998 11.8391 9.10409 11.8391 8.73911V8.6491C11.8391 8.12481 11.9659 7.71606 12.1692 7.38234C12.368 7.05615 12.6278 6.82251 12.8525 6.63516C12.8882 6.60547 12.9224 6.57706 12.9554 6.54976C13.3755 6.20159 13.5782 6.03359 13.5782 5.66664C13.5782 5.33551 13.4634 5.12442 13.3011 4.9889C13.1289 4.84511 12.8594 4.75085 12.5 4.75085C12.1491 4.75085 11.9156 4.82821 11.7629 4.93681C11.6155 5.04167 11.5065 5.20161 11.4496 5.44446C11.3844 5.72295 11.1375 5.97998 10.7918 5.97998C10.4134 5.97998 10.0555 5.63296 10.1657 5.19553C10.297 4.67462 10.5742 4.26139 10.9953 3.98566Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 11C11.75 10.5858 12.0858 10.25 12.5 10.25C12.9142 10.25 13.25 10.5858 13.25 11C13.25 11.4142 12.9142 11.75 12.5 11.75C12.0858 11.75 11.75 11.4142 11.75 11Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
6
images/icons/v3/person/person-questionmark-light.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.75 4.75C6.77687 4.75 5.75 5.72686 5.75 7.39062C5.75 8.20404 6.01052 8.94584 6.40454 9.46896C6.79952 9.99337 7.28423 10.25 7.75 10.25C8.21577 10.25 8.70048 9.99337 9.09546 9.46896C9.48948 8.94584 9.75 8.20404 9.75 7.39062C9.75 5.72686 8.72313 4.75 7.75 4.75ZM4.25 7.39062C4.25 5.18839 5.68556 3.25 7.75 3.25C9.81444 3.25 11.25 5.18839 11.25 7.39062C11.25 8.5102 10.8949 9.57309 10.2936 10.3714C9.69331 11.1684 8.80301 11.75 7.75 11.75C6.69699 11.75 5.80669 11.1684 5.20638 10.3714C4.60509 9.57309 4.25 8.5102 4.25 7.39062Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.89305 19.25H13.5216C13.2924 16.9479 10.9043 14.9421 7.70732 14.9421C4.51036 14.9421 2.1222 16.9479 1.89305 19.25ZM0.375 19.6128C0.375 16.0752 3.80059 13.4421 7.70732 13.4421C11.614 13.4421 15.0396 16.0752 15.0396 19.6128C15.0396 20.2877 14.4763 20.75 13.8854 20.75H1.5292C0.938363 20.75 0.375 20.2877 0.375 19.6128Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.7958 6.71595C17.3229 6.39327 17.9883 6.25 18.75 6.25C19.6611 6.25 20.4361 6.47344 20.9929 6.89822C21.5586 7.3298 21.875 7.95292 21.875 8.679C21.875 9.84723 21.1717 10.3971 20.5956 10.8474C20.5755 10.8631 20.5555 10.8787 20.5358 10.8942C20.2336 11.1313 19.9776 11.3409 19.7896 11.6097C19.6102 11.8661 19.4837 12.191 19.4837 12.6689V12.7663C19.4837 13.1715 19.1552 13.5 18.75 13.5C18.3448 13.5 18.0163 13.1715 18.0163 12.7663V12.6689C18.0163 12.0082 18.185 11.5003 18.4497 11.0895C18.7092 10.6868 19.0495 10.3963 19.3548 10.1556C19.4189 10.1051 19.4804 10.0575 19.5394 10.0118C19.7763 9.82823 19.9716 9.67691 20.1276 9.49989C20.3042 9.29938 20.4076 9.08755 20.4076 8.79167C20.4076 8.33101 20.2371 8.01536 19.9717 7.80578C19.6946 7.58706 19.2773 7.45614 18.75 7.45614C18.242 7.45614 17.8801 7.56169 17.6312 7.72751C17.3889 7.88893 17.2225 8.12794 17.1371 8.46296C17.0585 8.77158 16.7839 9.054 16.4037 9.054C15.9817 9.054 15.5857 8.6649 15.7185 8.18363C15.896 7.54046 16.2597 7.04414 16.7958 6.71595Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.875 16.125C17.875 15.6418 18.2668 15.25 18.75 15.25C19.2332 15.25 19.625 15.6418 19.625 16.125C19.625 16.6082 19.2332 17 18.75 17C18.2668 17 17.875 16.6082 17.875 16.125Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 16 KiB |
BIN
images/safety-tips/safety-tip-business.webp
Normal file
After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 22 KiB |
BIN
images/safety-tips/safety-tip-crypto.webp
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
images/safety-tips/safety-tip-fake.webp
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB |
BIN
images/safety-tips/safety-tip-links.webp
Normal file
After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 11 KiB |
BIN
images/safety-tips/safety-tip-vague.webp
Normal file
After Width: | Height: | Size: 4.5 KiB |
@@ -44,13 +44,10 @@
|
||||
flex-shrink: 0;
|
||||
|
||||
@mixin about-modal-icon($url) {
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg($url, variables.$color-black);
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg($url, variables.$color-gray-05);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
$url,
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&--profile {
|
||||
@@ -106,6 +103,18 @@
|
||||
&--note {
|
||||
@include about-modal-icon('../images/icons/v3/note/note.svg');
|
||||
}
|
||||
|
||||
&--group-question {
|
||||
@include about-modal-icon(
|
||||
'../images/icons/v3/group/group-questionmark-compact.svg'
|
||||
);
|
||||
}
|
||||
|
||||
&--direct-question {
|
||||
@include about-modal-icon(
|
||||
'../images/icons/v3/person/person-questionmark-compact.svg'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
|
@@ -33,17 +33,14 @@
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
background: variables.$color-gray-02;
|
||||
&::before {
|
||||
@include plus-icon(variables.$color-black);
|
||||
}
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
background: variables.$color-gray-90;
|
||||
&::before {
|
||||
@include plus-icon(variables.$color-gray-15);
|
||||
}
|
||||
background: light-dark(
|
||||
variables.$color-gray-02,
|
||||
variables.$color-gray-90
|
||||
);
|
||||
&::before {
|
||||
@include plus-icon(
|
||||
light-dark(variables.$color-black, variables.$color-gray-15)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,12 +60,7 @@
|
||||
|
||||
&__pending--info {
|
||||
@include mixins.font-subtitle;
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-60;
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-25;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
& {
|
||||
padding-block: 0;
|
||||
padding-inline: 28px;
|
||||
@@ -112,13 +104,7 @@
|
||||
$light-color: variables.$color-gray-75,
|
||||
$dark-color: variables.$color-gray-15
|
||||
) {
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg($url, $light-color);
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg($url, $dark-color);
|
||||
}
|
||||
@include mixins.color-svg($url, light-dark($light-color, $dark-color));
|
||||
}
|
||||
|
||||
&--color {
|
||||
@@ -171,6 +157,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--bell {
|
||||
&::after {
|
||||
@include details-icon('../images/icons/v3/bell/bell-compact.svg');
|
||||
}
|
||||
}
|
||||
|
||||
&--link {
|
||||
&::after {
|
||||
@include details-icon('../images/icons/v3/link/link.svg');
|
||||
@@ -215,13 +207,10 @@
|
||||
|
||||
&--down {
|
||||
border-radius: 18px;
|
||||
@include mixins.light-theme {
|
||||
background-color: variables.$color-gray-02;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
background-color: variables.$color-gray-90;
|
||||
}
|
||||
background-color: light-dark(
|
||||
variables.$color-gray-02,
|
||||
variables.$color-gray-90
|
||||
);
|
||||
|
||||
&::after {
|
||||
width: 18px;
|
||||
@@ -264,6 +253,24 @@
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&--help {
|
||||
&::after {
|
||||
@include details-icon('../images/icons/v3/help/help-light.svg');
|
||||
}
|
||||
}
|
||||
|
||||
&--invite {
|
||||
&::after {
|
||||
@include details-icon('../images/icons/v3/invite/invite.svg');
|
||||
}
|
||||
}
|
||||
|
||||
&--heart {
|
||||
&::after {
|
||||
@include details-icon('../images/icons/v3/heart/heart.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,13 +310,7 @@
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-95;
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-05;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-95, variables.$color-gray-05);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,13 +332,10 @@
|
||||
background: none;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
@include mixins.light-theme {
|
||||
background-color: variables.$color-gray-02;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
background-color: variables.$color-gray-90;
|
||||
}
|
||||
background-color: light-dark(
|
||||
variables.$color-gray-02,
|
||||
variables.$color-gray-90
|
||||
);
|
||||
|
||||
& .ConversationDetails-panel-row__actions {
|
||||
opacity: 1;
|
||||
@@ -376,14 +374,7 @@
|
||||
|
||||
&__info {
|
||||
margin-top: 4px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-60;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-25;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
}
|
||||
|
||||
&__right {
|
||||
@@ -410,15 +401,10 @@
|
||||
|
||||
&:not(:first-child)::before {
|
||||
border-top: 1px solid transparent;
|
||||
|
||||
@include mixins.light-theme {
|
||||
border-top-color: variables.$color-gray-15;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
border-top-color: variables.$color-gray-65;
|
||||
}
|
||||
|
||||
border-top-color: light-dark(
|
||||
variables.$color-gray-15,
|
||||
variables.$color-gray-65
|
||||
);
|
||||
& {
|
||||
content: '';
|
||||
display: block;
|
||||
@@ -495,48 +481,24 @@
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__ItemIcon--Audio {
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/phone/phone.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/phone/phone.svg',
|
||||
variables.$color-gray-15
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/phone/phone.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-15)
|
||||
);
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__ItemIcon--Video {
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/video/video.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/video/video.svg',
|
||||
variables.$color-gray-15
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/video/video.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-15)
|
||||
);
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__ItemIcon--Adhoc {
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/link/link.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/link/link.svg',
|
||||
variables.$color-gray-15
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/link/link.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-15)
|
||||
);
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__ItemLabel {
|
||||
@@ -558,18 +520,10 @@
|
||||
content: '';
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-down.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-down.svg',
|
||||
variables.$color-white
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-down.svg',
|
||||
light-dark(variables.$color-black, variables.$color-white)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,16 +534,8 @@
|
||||
.ConversationDetails--nickname-actions--delete {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/trash/trash.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/trash/trash.svg',
|
||||
variables.$color-white
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/trash/trash.svg',
|
||||
light-dark(variables.$color-black, variables.$color-white)
|
||||
);
|
||||
}
|
||||
|
@@ -34,18 +34,10 @@
|
||||
position: relative;
|
||||
inset-block-start: 2px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-right-bold.svg',
|
||||
variables.$color-gray-90
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-right-bold.svg',
|
||||
variables.$color-gray-05
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/chevron/chevron-right-bold.svg',
|
||||
light-dark(variables.$color-gray-90, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__profile-name {
|
||||
@@ -57,13 +49,7 @@
|
||||
margin-bottom: 2px;
|
||||
margin-top: 0;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-90;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-05;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-90, variables.$color-gray-05);
|
||||
}
|
||||
|
||||
&__with {
|
||||
@@ -73,13 +59,7 @@
|
||||
margin-bottom: 20px;
|
||||
max-width: 500px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-60;
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-25;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
}
|
||||
|
||||
&__note-to-self {
|
||||
@@ -88,12 +68,16 @@
|
||||
padding-block: 0;
|
||||
padding-inline: 16px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-60;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-60, variables.$color-gray-25);
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-25;
|
||||
&__members-count__button {
|
||||
@include mixins.button-reset;
|
||||
& {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-color: variables.$color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,12 +85,54 @@
|
||||
border-radius: 9999px;
|
||||
padding-block: 6px;
|
||||
padding-inline: 14px;
|
||||
margin-top: 12px;
|
||||
margin-top: 5px;
|
||||
@include mixins.font-subtitle;
|
||||
}
|
||||
|
||||
&__membership {
|
||||
@include mixins.font-body-2;
|
||||
&__review-carefully {
|
||||
@include mixins.font-body-2-bold;
|
||||
color: #a98b52;
|
||||
}
|
||||
|
||||
&__group-question-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 22px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group-questionmark-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__direct-question-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/person/person-questionmark-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__name-not-verified__button {
|
||||
@include mixins.button-reset;
|
||||
& {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
text-decoration-color: variables.$color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&--release-notes-notice {
|
||||
@include mixins.font-body-1;
|
||||
|
||||
user-select: none;
|
||||
|
||||
max-width: 255px;
|
||||
@@ -114,24 +140,72 @@
|
||||
padding-block: 16px;
|
||||
padding-inline: 20px;
|
||||
|
||||
border-radius: 18px;
|
||||
background-color: #e0e8fc;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
color: variables.$color-gray-75;
|
||||
}
|
||||
|
||||
&__release-notes-notice-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__release-notes-notice-check-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-inline-end: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/check/check-circle-fill.svg',
|
||||
variables.$color-borage-blue
|
||||
);
|
||||
}
|
||||
|
||||
&__release-notes-notice-bell-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
margin-inline-end: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/bell/bell-compact.svg',
|
||||
variables.$color-gray-75
|
||||
);
|
||||
}
|
||||
|
||||
&__membership {
|
||||
@include mixins.font-body-2;
|
||||
user-select: none;
|
||||
|
||||
max-width: 255px;
|
||||
margin-inline: auto;
|
||||
margin-block-start: 10px;
|
||||
padding-block: 16px;
|
||||
padding-inline: 20px;
|
||||
|
||||
border-radius: 18px;
|
||||
border-style: solid;
|
||||
border-width: 1.5px;
|
||||
border-width: 2.5px;
|
||||
|
||||
@include mixins.light-theme() {
|
||||
border-color: variables.$color-gray-05;
|
||||
}
|
||||
@include mixins.dark-theme() {
|
||||
border-color: variables.$color-gray-80;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
color: variables.$color-gray-90;
|
||||
}
|
||||
border-color: light-dark(
|
||||
variables.$color-gray-04,
|
||||
variables.$color-gray-80
|
||||
);
|
||||
|
||||
@include mixins.dark-theme {
|
||||
color: variables.$color-gray-02;
|
||||
}
|
||||
color: light-dark(variables.$color-gray-90, variables.$color-gray-02);
|
||||
|
||||
&__chevron {
|
||||
display: inline-block;
|
||||
@@ -140,19 +214,10 @@
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group.svg',
|
||||
variables.$color-black
|
||||
);
|
||||
}
|
||||
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group.svg',
|
||||
variables.$color-gray-05
|
||||
);
|
||||
}
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
&__name {
|
||||
@@ -160,38 +225,34 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__review-carefully-icon {
|
||||
display: inline-block;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/error/error-triangle-fill-compact-bold.svg',
|
||||
#a98b52
|
||||
);
|
||||
}
|
||||
|
||||
&__warning {
|
||||
line-height: 20px;
|
||||
|
||||
&__icon {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 18px;
|
||||
margin-inline-end: 8px;
|
||||
width: 18px;
|
||||
vertical-align: middle;
|
||||
|
||||
@include mixins.light-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/info/info.svg',
|
||||
variables.$color-gray-90
|
||||
);
|
||||
}
|
||||
@include mixins.dark-theme {
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/info/info.svg',
|
||||
variables.$color-gray-02
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&__learn-more {
|
||||
@include mixins.button-reset();
|
||||
& {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__members-count-icon {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 8px;
|
||||
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/group/group-compact.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
51
stylesheets/components/ProfileNameWarningModal.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
@use '../mixins';
|
||||
@use '../variables';
|
||||
|
||||
.ProfileNameWarningModal {
|
||||
.ProfileNameWarningModal__body_inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ProfileNameWarningModal__header-icon {
|
||||
display: block;
|
||||
align-self: center;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
margin-bottom: 24px;
|
||||
@include mixins.color-svg(
|
||||
'../images/icons/v3/person/person-questionmark-light.svg',
|
||||
light-dark(variables.$color-black, variables.$color-gray-05)
|
||||
);
|
||||
}
|
||||
|
||||
.ProfileNameWarningModal__description {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.ProfileNameWarningModal__list {
|
||||
padding-inline-start: 24px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.ProfileNameWarningModal__list-item {
|
||||
position: relative;
|
||||
padding-inline-start: 17px;
|
||||
margin-bottom: 25px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.ProfileNameWarningModal__list-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
background-color: variables.$color-gray-20;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
@@ -144,6 +144,7 @@
|
||||
@use 'components/PlaybackRateButton.scss';
|
||||
@use 'components/Preferences.scss';
|
||||
@use 'components/ProfileEditor.scss';
|
||||
@use 'components/ProfileNameWarningModal.scss';
|
||||
@use 'components/ProgressBar.scss';
|
||||
@use 'components/ProgressCircle.scss';
|
||||
@use 'components/Quote.scss';
|
||||
|
@@ -100,6 +100,7 @@ export type OwnProps = Readonly<{
|
||||
areWeAdmin: boolean | null;
|
||||
areWePending: boolean | null;
|
||||
areWePendingApproval: boolean | null;
|
||||
sharedGroupNames?: ReadonlyArray<string>;
|
||||
cancelRecording: () => unknown;
|
||||
completeRecording: (
|
||||
conversationId: string,
|
||||
@@ -350,6 +351,7 @@ export const CompositionArea = memo(function CompositionArea({
|
||||
// SMS-only contacts
|
||||
isSmsOnlyOrUnregistered,
|
||||
isFetchingUUID,
|
||||
sharedGroupNames,
|
||||
renderSmartCompositionRecording,
|
||||
renderSmartCompositionRecordingDraft,
|
||||
// Selected messages
|
||||
@@ -893,6 +895,7 @@ export const CompositionArea = memo(function CompositionArea({
|
||||
isBlocked={isBlocked}
|
||||
isHidden={isHidden}
|
||||
isReported={isReported}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
acceptConversation={acceptConversation}
|
||||
reportSpam={reportSpam}
|
||||
blockAndReportSpam={blockAndReportSpam}
|
||||
|
@@ -11,12 +11,16 @@ import { I18n } from './I18n';
|
||||
import { LeftPaneDialog } from './LeftPaneDialog';
|
||||
import type { WidthBreakpoint } from './_util';
|
||||
import { formatFileSize } from '../util/formatFileSize';
|
||||
import { getLocalizedUrl } from '../util/getLocalizedUrl';
|
||||
|
||||
function contactSupportLink(parts: ReactNode): JSX.Element {
|
||||
const localizedSupportLink = getLocalizedUrl(
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop'
|
||||
);
|
||||
return (
|
||||
<a
|
||||
key="signal-support"
|
||||
href="https://support.signal.org/hc/en-us/requests/new?desktop"
|
||||
href={localizedSupportLink}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
@@ -133,6 +133,9 @@ export type PropsType = {
|
||||
// UsernameOnboarding
|
||||
usernameOnboardingState: UsernameOnboardingState;
|
||||
renderUsernameOnboarding: () => JSX.Element;
|
||||
isProfileNameWarningModalVisible: boolean;
|
||||
profileNameWarningModalConversationType?: string;
|
||||
renderProfileNameWarningModal: () => JSX.Element;
|
||||
};
|
||||
|
||||
export function GlobalModalContainer({
|
||||
@@ -220,6 +223,9 @@ export function GlobalModalContainer({
|
||||
// UsernameOnboarding
|
||||
usernameOnboardingState,
|
||||
renderUsernameOnboarding,
|
||||
// ProfileNameWarningModal
|
||||
isProfileNameWarningModalVisible,
|
||||
renderProfileNameWarningModal,
|
||||
}: PropsType): JSX.Element | null {
|
||||
// We want the following dialogs to show in this order:
|
||||
// 1. Errors
|
||||
@@ -296,6 +302,10 @@ export function GlobalModalContainer({
|
||||
return renderProfileEditor();
|
||||
}
|
||||
|
||||
if (isProfileNameWarningModalVisible) {
|
||||
return renderProfileNameWarningModal();
|
||||
}
|
||||
|
||||
if (isShortcutGuideModalVisible) {
|
||||
return renderShortcutGuideModal();
|
||||
}
|
||||
|
@@ -19,29 +19,35 @@ export function SafetyTipsModal({
|
||||
}: SafetyTipsModalProps): JSX.Element {
|
||||
const pages = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: 'fake',
|
||||
title: i18n('icu:SafetyTipsModal__TipTitle--Fake'),
|
||||
description: i18n('icu:SafetyTipsModal__TipDescription--Fake'),
|
||||
imageUrl: 'images/safety-tips/safety-tip-fake.webp',
|
||||
},
|
||||
{
|
||||
key: 'crypto',
|
||||
title: i18n('icu:SafetyTipsModal__TipTitle--Crypto'),
|
||||
description: i18n('icu:SafetyTipsModal__TipDescription--Crypto'),
|
||||
imageUrl: 'images/safety-tips/safety-tip-crypto.png',
|
||||
imageUrl: 'images/safety-tips/safety-tip-crypto.webp',
|
||||
},
|
||||
{
|
||||
key: 'vague',
|
||||
title: i18n('icu:SafetyTipsModal__TipTitle--Vague'),
|
||||
description: i18n('icu:SafetyTipsModal__TipDescription--Vague'),
|
||||
imageUrl: 'images/safety-tips/safety-tip-vague.png',
|
||||
imageUrl: 'images/safety-tips/safety-tip-vague.webp',
|
||||
},
|
||||
{
|
||||
key: 'links',
|
||||
title: i18n('icu:SafetyTipsModal__TipTitle--Links'),
|
||||
description: i18n('icu:SafetyTipsModal__TipDescription--Links'),
|
||||
imageUrl: 'images/safety-tips/safety-tip-links.png',
|
||||
imageUrl: 'images/safety-tips/safety-tip-links.webp',
|
||||
},
|
||||
{
|
||||
key: 'business',
|
||||
title: i18n('icu:SafetyTipsModal__TipTitle--Business'),
|
||||
description: i18n('icu:SafetyTipsModal__TipDescription--Business'),
|
||||
imageUrl: 'images/safety-tips/safety-tip-business.png',
|
||||
imageUrl: 'images/safety-tips/safety-tip-business.webp',
|
||||
},
|
||||
];
|
||||
}, [i18n]);
|
||||
|
@@ -66,9 +66,11 @@ export default {
|
||||
onOpenNotePreviewModal: action('onOpenNotePreviewModal'),
|
||||
toggleSignalConnectionsModal: action('toggleSignalConnections'),
|
||||
toggleSafetyNumberModal: action('toggleSafetyNumberModal'),
|
||||
toggleProfileNameWarningModal: action('toggleProfileNameWarningModal'),
|
||||
updateSharedGroups: action('updateSharedGroups'),
|
||||
unblurAvatar: action('unblurAvatar'),
|
||||
conversation,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
isSignalConnection: false,
|
||||
},
|
||||
} satisfies ComponentMeta<PropsType>;
|
||||
@@ -124,3 +126,13 @@ export function WithSharedGroups(args: PropsType): JSX.Element {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function DirectFromTrustedContact(args: PropsType): JSX.Element {
|
||||
return (
|
||||
<AboutContactModal
|
||||
{...args}
|
||||
conversation={conversation}
|
||||
fromOrAddedByTrustedContact
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { type ReactNode, useCallback, useEffect } from 'react';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { isInSystemContacts } from '../../util/isInSystemContacts';
|
||||
@@ -26,9 +26,11 @@ export type PropsType = Readonly<{
|
||||
onClose: () => void;
|
||||
onOpenNotePreviewModal: () => void;
|
||||
conversation: ConversationType;
|
||||
fromOrAddedByTrustedContact?: boolean;
|
||||
isSignalConnection: boolean;
|
||||
toggleSignalConnectionsModal: () => void;
|
||||
toggleSafetyNumberModal: (id: string) => void;
|
||||
toggleProfileNameWarningModal: () => void;
|
||||
updateSharedGroups: (id: string) => void;
|
||||
unblurAvatar: (conversationId: string) => void;
|
||||
}>;
|
||||
@@ -36,9 +38,11 @@ export type PropsType = Readonly<{
|
||||
export function AboutContactModal({
|
||||
i18n,
|
||||
conversation,
|
||||
fromOrAddedByTrustedContact,
|
||||
isSignalConnection,
|
||||
toggleSignalConnectionsModal,
|
||||
toggleSafetyNumberModal,
|
||||
toggleProfileNameWarningModal,
|
||||
updateSharedGroups,
|
||||
unblurAvatar,
|
||||
onClose,
|
||||
@@ -77,6 +81,14 @@ export function AboutContactModal({
|
||||
[toggleSafetyNumberModal, conversation.id]
|
||||
);
|
||||
|
||||
const onProfileNameWarningClick = useCallback(
|
||||
(ev: React.MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
toggleProfileNameWarningModal();
|
||||
},
|
||||
[toggleProfileNameWarningModal]
|
||||
);
|
||||
|
||||
let statusRow: JSX.Element | undefined;
|
||||
|
||||
if (isMe) {
|
||||
@@ -185,6 +197,32 @@ export function AboutContactModal({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isMe && !fromOrAddedByTrustedContact ? (
|
||||
<div className="AboutContactModal__row">
|
||||
<i
|
||||
className={`AboutContactModal__row__icon AboutContactModal__row__icon--${conversation.type === 'group' ? 'group' : 'direct'}-question`}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="AboutContactModal__button"
|
||||
onClick={onProfileNameWarningClick}
|
||||
>
|
||||
<I18n
|
||||
components={{
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
clickable: (parts: ReactNode) => <>{parts}</>,
|
||||
}}
|
||||
i18n={i18n}
|
||||
id={
|
||||
conversation.type === 'group'
|
||||
? 'icu:ConversationHero--group-names'
|
||||
: 'icu:ConversationHero--profile-names'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isMe && conversation.isVerified ? (
|
||||
<div className="AboutContactModal__row">
|
||||
<i className="AboutContactModal__row__icon AboutContactModal__row__icon--verified" />
|
||||
|
@@ -10,7 +10,7 @@ import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||
import { SystemMessage } from './SystemMessage';
|
||||
import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog';
|
||||
import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
||||
import { mapToSupportLocale } from '../../util/mapToSupportLocale';
|
||||
import { getLocalizedUrl } from '../../util/getLocalizedUrl';
|
||||
|
||||
type PropsHousekeepingType = {
|
||||
i18n: LocalizerType;
|
||||
@@ -34,11 +34,9 @@ export function ChatSessionRefreshedNotification(
|
||||
const wrappedContactSupport = useCallback(() => {
|
||||
setIsDialogOpen(false);
|
||||
|
||||
const baseUrl =
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop&chat_refreshed';
|
||||
const locale = window.SignalContext.getResolvedMessagesLocale();
|
||||
const supportLocale = mapToSupportLocale(locale);
|
||||
const url = baseUrl.replace('LOCALE', supportLocale);
|
||||
const url = getLocalizedUrl(
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop&chat_refreshed'
|
||||
);
|
||||
|
||||
openLinkInWebBrowser(url);
|
||||
}, [setIsDialogOpen]);
|
||||
|
@@ -147,6 +147,7 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
|
||||
case MessageRequestState.reportingAndMaybeBlocking:
|
||||
case MessageRequestState.acceptedOptions:
|
||||
case MessageRequestState.unblocking:
|
||||
case MessageRequestState.accepting:
|
||||
assertDev(
|
||||
false,
|
||||
`Got unexpected MessageRequestState.${MessageRequestState[messageRequestState]} state. Clearing confiration state`
|
||||
|
@@ -19,12 +19,16 @@ export default {
|
||||
component: ConversationHero,
|
||||
args: {
|
||||
conversationType: 'direct',
|
||||
fromOrAddedByTrustedContact: true,
|
||||
i18n,
|
||||
isDirectConvoAndHasNickname: false,
|
||||
theme: ThemeType.light,
|
||||
unblurAvatar: action('unblurAvatar'),
|
||||
updateSharedGroups: action('updateSharedGroups'),
|
||||
viewUserStories: action('viewUserStories'),
|
||||
toggleAboutContactModal: action('toggleAboutContactModal'),
|
||||
toggleProfileNameWarningModal: action('toggleProfileNameWarningModal'),
|
||||
openConversationDetails: action('openConversationDetails'),
|
||||
},
|
||||
} satisfies Meta<Props>;
|
||||
|
||||
@@ -73,6 +77,12 @@ DirectNoGroupsJustProfile.args = {
|
||||
phoneNumber: casual.phone,
|
||||
};
|
||||
|
||||
export const SignalConversation = Template.bind({});
|
||||
SignalConversation.args = {
|
||||
isSignalConversation: true,
|
||||
phoneNumber: casual.phone,
|
||||
};
|
||||
|
||||
export const DirectNoGroupsJustPhoneNumber = Template.bind({});
|
||||
DirectNoGroupsJustPhoneNumber.args = {
|
||||
phoneNumber: casual.phone,
|
||||
@@ -146,6 +156,15 @@ GroupNoName.args = {
|
||||
title: '',
|
||||
};
|
||||
|
||||
export const GroupNotAccepted = Template.bind({});
|
||||
GroupNotAccepted.args = {
|
||||
conversationType: 'group',
|
||||
groupDescription: casual.sentence,
|
||||
membersCount: casual.integer(20, 100),
|
||||
title: casual.title,
|
||||
acceptedMessageRequest: false,
|
||||
};
|
||||
|
||||
export const NoteToSelf = Template.bind({});
|
||||
NoteToSelf.args = {
|
||||
isMe: true,
|
||||
@@ -160,3 +179,26 @@ export const ReadStories = Template.bind({});
|
||||
ReadStories.args = {
|
||||
hasStories: HasStories.Read,
|
||||
};
|
||||
|
||||
export const DirectNotFromTrustedContact = Template.bind({});
|
||||
DirectNotFromTrustedContact.args = {
|
||||
conversationType: 'direct',
|
||||
title: casual.full_name,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
};
|
||||
|
||||
export const DirectWithNickname = Template.bind({});
|
||||
DirectWithNickname.args = {
|
||||
conversationType: 'direct',
|
||||
title: casual.full_name,
|
||||
fromOrAddedByTrustedContact: false,
|
||||
isDirectConvoAndHasNickname: true,
|
||||
};
|
||||
|
||||
export const GroupNotFromTrustedContact = Template.bind({});
|
||||
GroupNotFromTrustedContact.args = {
|
||||
conversationType: 'group',
|
||||
title: casual.title,
|
||||
membersCount: casual.integer(5, 20),
|
||||
fromOrAddedByTrustedContact: false,
|
||||
};
|
||||
|
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { type ReactNode, useEffect, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { Props as AvatarProps } from '../Avatar';
|
||||
import { Avatar, AvatarSize, AvatarBlur } from '../Avatar';
|
||||
import { ContactName } from './ContactName';
|
||||
@@ -12,22 +13,24 @@ import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type { HasStories } from '../../types/Stories';
|
||||
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
||||
import { StoryViewModeType } from '../../types/Stories';
|
||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
import { shouldBlurAvatar } from '../../util/shouldBlurAvatar';
|
||||
import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
||||
import { Button, ButtonVariant } from '../Button';
|
||||
import { SafetyTipsModal } from '../SafetyTipsModal';
|
||||
import { I18n } from '../I18n';
|
||||
|
||||
export type Props = {
|
||||
about?: string;
|
||||
acceptedMessageRequest?: boolean;
|
||||
fromOrAddedByTrustedContact?: boolean;
|
||||
groupDescription?: string;
|
||||
hasStories?: HasStories;
|
||||
id: string;
|
||||
i18n: LocalizerType;
|
||||
isDirectConvoAndHasNickname?: boolean;
|
||||
isMe: boolean;
|
||||
isSignalConversation?: boolean;
|
||||
membersCount?: number;
|
||||
openConversationDetails?: () => unknown;
|
||||
phoneNumber?: string;
|
||||
sharedGroupNames?: ReadonlyArray<string>;
|
||||
unblurAvatar: (conversationId: string) => void;
|
||||
@@ -36,30 +39,39 @@ export type Props = {
|
||||
theme: ThemeType;
|
||||
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||
toggleAboutContactModal: (conversationId: string) => unknown;
|
||||
toggleProfileNameWarningModal: (conversationType?: string) => unknown;
|
||||
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
||||
|
||||
const renderMembershipRow = ({
|
||||
const renderExtraInformation = ({
|
||||
acceptedMessageRequest,
|
||||
conversationType,
|
||||
fromOrAddedByTrustedContact,
|
||||
i18n,
|
||||
isDirectConvoAndHasNickname,
|
||||
isMe,
|
||||
onClickMessageRequestWarning,
|
||||
membersCount,
|
||||
onClickProfileNameWarning,
|
||||
onToggleSafetyTips,
|
||||
openConversationDetails,
|
||||
phoneNumber,
|
||||
sharedGroupNames,
|
||||
}: Pick<
|
||||
Props,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'conversationType'
|
||||
| 'fromOrAddedByTrustedContact'
|
||||
| 'i18n'
|
||||
| 'isDirectConvoAndHasNickname'
|
||||
| 'isMe'
|
||||
| 'membersCount'
|
||||
| 'openConversationDetails'
|
||||
| 'phoneNumber'
|
||||
> &
|
||||
Required<Pick<Props, 'sharedGroupNames'>> & {
|
||||
onClickMessageRequestWarning: () => void;
|
||||
onClickProfileNameWarning: () => void;
|
||||
onToggleSafetyTips: (showSafetyTips: boolean) => void;
|
||||
}) => {
|
||||
if (conversationType !== 'direct') {
|
||||
if (conversationType !== 'direct' && conversationType !== 'group') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -71,7 +83,7 @@ const renderMembershipRow = ({
|
||||
);
|
||||
}
|
||||
|
||||
const safetyTipsButton = (
|
||||
const safetyTipsButton = !acceptedMessageRequest ? (
|
||||
<div>
|
||||
<Button
|
||||
className="module-conversation-hero__safety-tips-button"
|
||||
@@ -83,55 +95,137 @@ const renderMembershipRow = ({
|
||||
{i18n('icu:MessageRequestWarning__safety-tips')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
) : null;
|
||||
|
||||
if (sharedGroupNames.length > 0) {
|
||||
return (
|
||||
<div className="module-conversation-hero__membership">
|
||||
const shouldShowReviewCarefully =
|
||||
!acceptedMessageRequest &&
|
||||
(conversationType === 'group' || sharedGroupNames.length <= 1);
|
||||
|
||||
const reviewCarefullyLabel = shouldShowReviewCarefully ? (
|
||||
<div className="module-conversation-hero__review-carefully">
|
||||
<i className="module-conversation-hero__membership__review-carefully-icon" />
|
||||
{i18n('icu:ConversationHero--review-carefully')}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const sharedGroupsLabel =
|
||||
conversationType === 'direct' ? (
|
||||
<div>
|
||||
<i className="module-conversation-hero__membership__chevron" />
|
||||
<SharedGroupNames
|
||||
i18n={i18n}
|
||||
nameClassName="module-conversation-hero__membership__name"
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
/>
|
||||
{safetyTipsButton}
|
||||
</div>
|
||||
);
|
||||
) : null;
|
||||
|
||||
const nameNotVerifiedLabel =
|
||||
!fromOrAddedByTrustedContact && !isDirectConvoAndHasNickname ? (
|
||||
<div className="module-conversation-hero__name-not-verified">
|
||||
<i
|
||||
className={classNames({
|
||||
'module-conversation-hero__group-question-icon':
|
||||
conversationType === 'group',
|
||||
'module-conversation-hero__direct-question-icon':
|
||||
conversationType === 'direct',
|
||||
})}
|
||||
/>
|
||||
<I18n
|
||||
components={{
|
||||
clickable: (parts: ReactNode) => (
|
||||
<button
|
||||
className="module-conversation-hero__name-not-verified__button"
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
onClickProfileNameWarning();
|
||||
}}
|
||||
>
|
||||
{parts}
|
||||
</button>
|
||||
),
|
||||
}}
|
||||
i18n={i18n}
|
||||
id={
|
||||
conversationType === 'group'
|
||||
? 'icu:ConversationHero--group-names'
|
||||
: 'icu:ConversationHero--profile-names'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const membersCountLabel =
|
||||
conversationType === 'group' && membersCount != null ? (
|
||||
<div className="module-conversation-hero__membership__members-count">
|
||||
<i className="module-conversation-hero__members-count-icon" />
|
||||
<button
|
||||
className="module-conversation-hero__members-count__button"
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
if (openConversationDetails) {
|
||||
openConversationDetails();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n('icu:ConversationHero--members', { count: membersCount })}
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
if (
|
||||
conversationType === 'direct' &&
|
||||
sharedGroupNames.length === 0 &&
|
||||
acceptedMessageRequest &&
|
||||
phoneNumber
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (acceptedMessageRequest) {
|
||||
if (phoneNumber) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="module-conversation-hero__membership">
|
||||
{i18n('icu:no-groups-in-common')}
|
||||
{safetyTipsButton}
|
||||
</div>
|
||||
);
|
||||
|
||||
// Check if we should show anything at all
|
||||
const shouldShowAnything =
|
||||
Boolean(reviewCarefullyLabel) ||
|
||||
Boolean(nameNotVerifiedLabel) ||
|
||||
Boolean(sharedGroupsLabel) ||
|
||||
Boolean(safetyTipsButton) ||
|
||||
Boolean(membersCountLabel);
|
||||
|
||||
if (!shouldShowAnything) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="module-conversation-hero__membership">
|
||||
<div className="module-conversation-hero__membership__warning">
|
||||
<i className="module-conversation-hero__membership__warning__icon" />
|
||||
<span>{i18n('icu:no-groups-in-common-warning')}</span>
|
||||
|
||||
<button
|
||||
className="module-conversation-hero__membership__warning__learn-more"
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
onClickMessageRequestWarning();
|
||||
}}
|
||||
>
|
||||
{i18n('icu:MessageRequestWarning__learn-more')}
|
||||
</button>
|
||||
</div>
|
||||
{reviewCarefullyLabel}
|
||||
{nameNotVerifiedLabel}
|
||||
{sharedGroupsLabel}
|
||||
{membersCountLabel}
|
||||
{safetyTipsButton}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function ReleaseNotesExtraInformation({
|
||||
i18n,
|
||||
}: {
|
||||
i18n: LocalizerType;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="module-conversation-hero--release-notes-notice">
|
||||
<div className="module-conversation-hero__release-notes-notice-content">
|
||||
<i className="module-conversation-hero__release-notes-notice-check-icon" />
|
||||
{i18n('icu:ConversationHero--signal-official-chat')}
|
||||
</div>
|
||||
<div className="module-conversation-hero__release-notes-notice-content">
|
||||
<i className="module-conversation-hero__release-notes-notice-bell-icon" />
|
||||
{i18n('icu:ConversationHero--release-notes')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ConversationHero({
|
||||
i18n,
|
||||
about,
|
||||
@@ -140,10 +234,13 @@ export function ConversationHero({
|
||||
badge,
|
||||
color,
|
||||
conversationType,
|
||||
fromOrAddedByTrustedContact,
|
||||
groupDescription,
|
||||
hasStories,
|
||||
id,
|
||||
isDirectConvoAndHasNickname,
|
||||
isMe,
|
||||
openConversationDetails,
|
||||
isSignalConversation,
|
||||
membersCount,
|
||||
sharedGroupNames = [],
|
||||
@@ -156,13 +253,9 @@ export function ConversationHero({
|
||||
updateSharedGroups,
|
||||
viewUserStories,
|
||||
toggleAboutContactModal,
|
||||
toggleProfileNameWarningModal,
|
||||
}: Props): JSX.Element {
|
||||
const [isShowingSafetyTips, setIsShowingSafetyTips] = useState(false);
|
||||
const [isShowingMessageRequestWarning, setIsShowingMessageRequestWarning] =
|
||||
useState(false);
|
||||
const closeMessageRequestWarning = () => {
|
||||
setIsShowingMessageRequestWarning(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Kick off the expensive hydration of the current sharedGroupNames
|
||||
@@ -215,7 +308,6 @@ export function ConversationHero({
|
||||
);
|
||||
}
|
||||
|
||||
/* eslint-disable no-nested-ternary */
|
||||
return (
|
||||
<>
|
||||
<div className="module-conversation-hero">
|
||||
@@ -248,55 +340,36 @@ export function ConversationHero({
|
||||
<About text={about} />
|
||||
</div>
|
||||
)}
|
||||
{!isMe ? (
|
||||
{!isMe && groupDescription ? (
|
||||
<div className="module-conversation-hero__with">
|
||||
{groupDescription ? (
|
||||
<GroupDescription
|
||||
i18n={i18n}
|
||||
title={title}
|
||||
text={groupDescription}
|
||||
/>
|
||||
) : membersCount != null ? (
|
||||
i18n('icu:ConversationHero--members', { count: membersCount })
|
||||
) : null}
|
||||
<GroupDescription
|
||||
i18n={i18n}
|
||||
title={title}
|
||||
text={groupDescription}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{!isSignalConversation &&
|
||||
renderMembershipRow({
|
||||
renderExtraInformation({
|
||||
acceptedMessageRequest,
|
||||
conversationType,
|
||||
fromOrAddedByTrustedContact,
|
||||
i18n,
|
||||
isDirectConvoAndHasNickname,
|
||||
isMe,
|
||||
onClickMessageRequestWarning() {
|
||||
setIsShowingMessageRequestWarning(true);
|
||||
membersCount,
|
||||
onClickProfileNameWarning() {
|
||||
toggleProfileNameWarningModal(conversationType);
|
||||
},
|
||||
onToggleSafetyTips(showSafetyTips: boolean) {
|
||||
setIsShowingSafetyTips(showSafetyTips);
|
||||
},
|
||||
openConversationDetails,
|
||||
phoneNumber,
|
||||
sharedGroupNames,
|
||||
})}
|
||||
{isSignalConversation && <ReleaseNotesExtraInformation i18n={i18n} />}
|
||||
</div>
|
||||
{isShowingMessageRequestWarning && (
|
||||
<ConfirmationDialog
|
||||
dialogName="ConversationHere.messageRequestWarning"
|
||||
i18n={i18n}
|
||||
onClose={closeMessageRequestWarning}
|
||||
actions={[
|
||||
{
|
||||
text: i18n('icu:MessageRequestWarning__dialog__learn-even-more'),
|
||||
action: () => {
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/360007459591'
|
||||
);
|
||||
closeMessageRequestWarning();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{i18n('icu:MessageRequestWarning__dialog__details')}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
|
||||
{isShowingSafetyTips && (
|
||||
<SafetyTipsModal
|
||||
@@ -308,5 +381,4 @@ export function ConversationHero({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
/* eslint-enable no-nested-ternary */
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import { strictAssert } from '../../util/assert';
|
||||
export type Props = {
|
||||
i18n: LocalizerType;
|
||||
isHidden: boolean | null;
|
||||
sharedGroupNames?: ReadonlyArray<string>;
|
||||
} & Omit<
|
||||
MessageRequestActionsConfirmationProps,
|
||||
'i18n' | 'state' | 'onChangeState'
|
||||
@@ -30,6 +31,7 @@ export function MessageRequestActions({
|
||||
isBlocked,
|
||||
isHidden,
|
||||
isReported,
|
||||
sharedGroupNames = [],
|
||||
acceptConversation,
|
||||
blockAndReportSpam,
|
||||
blockConversation,
|
||||
@@ -153,7 +155,16 @@ export function MessageRequestActions({
|
||||
)}
|
||||
{!isBlocked ? (
|
||||
<Button
|
||||
onClick={() => acceptConversation(conversationId)}
|
||||
onClick={() => {
|
||||
if (
|
||||
conversationType === 'direct' &&
|
||||
sharedGroupNames.length > 1
|
||||
) {
|
||||
acceptConversation(conversationId);
|
||||
} else {
|
||||
setMrState(MessageRequestState.accepting);
|
||||
}
|
||||
}}
|
||||
variant={ButtonVariant.SecondaryAffirmative}
|
||||
>
|
||||
{i18n('icu:MessageRequests--accept')}
|
||||
|
@@ -14,6 +14,7 @@ export enum MessageRequestState {
|
||||
unblocking,
|
||||
reportingAndMaybeBlocking,
|
||||
acceptedOptions,
|
||||
accepting,
|
||||
default,
|
||||
}
|
||||
|
||||
@@ -282,5 +283,29 @@ export function MessageRequestActionsConfirmation({
|
||||
);
|
||||
}
|
||||
|
||||
if (state === MessageRequestState.accepting) {
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
key="messageRequestActionsConfirmation.accepting"
|
||||
dialogName="messageRequestActionsConfirmation.accepting"
|
||||
moduleClassName="MessageRequestActionsConfirmation"
|
||||
i18n={i18n}
|
||||
onClose={() => {
|
||||
onChangeState(MessageRequestState.default);
|
||||
}}
|
||||
title={i18n('icu:MessageRequests--accept-confirm-title')}
|
||||
actions={[
|
||||
{
|
||||
text: i18n('icu:MessageRequests--accept'),
|
||||
action: () => acceptConversation(conversationId),
|
||||
style: 'affirmative',
|
||||
},
|
||||
]}
|
||||
>
|
||||
{i18n('icu:MessageRequests--accept-confirm-body')}
|
||||
</ConfirmationDialog>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { PropsType } from './ProfileNameWarningModal';
|
||||
import { ProfileNameWarningModal } from './ProfileNameWarningModal';
|
||||
import { type ComponentMeta } from '../../storybook/types';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
export default {
|
||||
title: 'Components/Conversation/ProfileNameWarningModal',
|
||||
component: ProfileNameWarningModal,
|
||||
args: {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
conversationType: 'direct',
|
||||
},
|
||||
} satisfies ComponentMeta<PropsType>;
|
||||
|
||||
export function Direct(args: PropsType): JSX.Element {
|
||||
return <ProfileNameWarningModal {...args} conversationType="direct" />;
|
||||
}
|
||||
|
||||
export function Group(args: PropsType): JSX.Element {
|
||||
return <ProfileNameWarningModal {...args} conversationType="group" />;
|
||||
}
|
70
ts/components/conversation/ProfileNameWarningModal.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { Modal } from '../Modal';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
conversationType: 'group' | 'direct';
|
||||
i18n: LocalizerType;
|
||||
onClose: () => void;
|
||||
}>;
|
||||
|
||||
const DESCRIPTION_KEYS = {
|
||||
direct: 'icu:ProfileNameWarningModal__description--direct',
|
||||
group: 'icu:ProfileNameWarningModal__description--group',
|
||||
} as const;
|
||||
|
||||
const LIST_ITEM_KEYS = {
|
||||
item1: {
|
||||
direct: 'icu:ProfileNameWarningModal__list--item1--direct',
|
||||
group: 'icu:ProfileNameWarningModal__list--item1--group',
|
||||
},
|
||||
item2: {
|
||||
direct: 'icu:ProfileNameWarningModal__list--item2--direct',
|
||||
group: 'icu:ProfileNameWarningModal__list--item2--group',
|
||||
},
|
||||
item3: {
|
||||
direct: 'icu:ProfileNameWarningModal__list--item3--direct',
|
||||
group: 'icu:ProfileNameWarningModal__list--item3--group',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export function ProfileNameWarningModal({
|
||||
conversationType,
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return (
|
||||
<Modal
|
||||
modalName="ProfileNameWarningModal"
|
||||
moduleClassName="ProfileNameWarningModal"
|
||||
hasXButton
|
||||
i18n={i18n}
|
||||
onClose={onClose}
|
||||
>
|
||||
<i className="ProfileNameWarningModal__header-icon" />
|
||||
<div className="ProfileNameWarningModal__description">
|
||||
{i18n(DESCRIPTION_KEYS[conversationType])}
|
||||
</div>
|
||||
<ul className="ProfileNameWarningModal__list">
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item1[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item2[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
<li className="ProfileNameWarningModal__list-item">
|
||||
<span className="ProfileNameWarningModal__list-item-text">
|
||||
{i18n(LIST_ITEM_KEYS.item3[conversationType])}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@@ -420,6 +420,7 @@ const renderHeroRow = () => {
|
||||
updateSharedGroups={noop}
|
||||
viewUserStories={action('viewUserStories')}
|
||||
toggleAboutContactModal={action('toggleAboutContactModal')}
|
||||
toggleProfileNameWarningModal={action('toggleProfileNameWarningModal')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import type { SmartChooseGroupMembersModalPropsType } from '../../../state/smart
|
||||
import type { SmartConfirmAdditionsModalPropsType } from '../../../state/smart/ConfirmAdditionsModal';
|
||||
import { assertDev } from '../../../util/assert';
|
||||
import { getMutedUntilText } from '../../../util/getMutedUntilText';
|
||||
import { getLocalizedUrl } from '../../../util/getLocalizedUrl';
|
||||
|
||||
import type { LocalizerType, ThemeType } from '../../../types/Util';
|
||||
import type { BadgeType } from '../../../badges/types';
|
||||
@@ -40,6 +41,7 @@ import type {
|
||||
import { EditConversationAttributesModal } from './EditConversationAttributesModal';
|
||||
import { RequestState } from './util';
|
||||
import { getCustomColorStyle } from '../../../util/getCustomColorStyle';
|
||||
import { openLinkInWebBrowser } from '../../../util/openLinkInWebBrowser';
|
||||
import { ConfirmationDialog } from '../../ConfirmationDialog';
|
||||
import { ConversationNotificationsModal } from './ConversationNotificationsModal';
|
||||
import type {
|
||||
@@ -60,9 +62,11 @@ import {
|
||||
InAnotherCallTooltip,
|
||||
getTooltipContent,
|
||||
} from '../InAnotherCallTooltip';
|
||||
import { BadgeSustainerInstructionsDialog } from '../../BadgeSustainerInstructionsDialog';
|
||||
|
||||
enum ModalState {
|
||||
AddingGroupMembers,
|
||||
BecomeSustainer,
|
||||
ConfirmDeleteNicknameAndNote,
|
||||
EditingGroupDescription,
|
||||
EditingGroupTitle,
|
||||
@@ -245,6 +249,11 @@ export function ConversationDetails({
|
||||
case ModalState.NothingOpen:
|
||||
modalNode = undefined;
|
||||
break;
|
||||
case ModalState.BecomeSustainer:
|
||||
modalNode = (
|
||||
<BadgeSustainerInstructionsDialog i18n={i18n} onClose={onCloseModal} />
|
||||
);
|
||||
break;
|
||||
case ModalState.EditingGroupDescription:
|
||||
case ModalState.EditingGroupTitle:
|
||||
modalNode = (
|
||||
@@ -473,6 +482,74 @@ export function ConversationDetails({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isSignalConversation && (
|
||||
<>
|
||||
<PanelSection>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:ConversationHero--signal-official-chat')}
|
||||
icon={IconType.timer}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:ConversationHero--signal-official-chat')}
|
||||
/>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:ConversationHero--release-notes')}
|
||||
icon={IconType.bell}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:ConversationHero--release-notes')}
|
||||
/>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title={i18n('icu:ConversationDetails--help-section')}>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:ConversationDetails--support-center')}
|
||||
icon={IconType.help}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:ConversationDetails--support-center')}
|
||||
onClick={() => {
|
||||
openLinkInWebBrowser(
|
||||
getLocalizedUrl('https://support.signal.org/hc/LOCALE')
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:contactUs')}
|
||||
icon={IconType.invite}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:contactUs')}
|
||||
onClick={() => {
|
||||
openLinkInWebBrowser(
|
||||
getLocalizedUrl(
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop'
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<PanelRow
|
||||
icon={
|
||||
<ConversationDetailsIcon
|
||||
ariaLabel={i18n('icu:BadgeDialog__become-a-sustainer-button')}
|
||||
icon={IconType.heart}
|
||||
/>
|
||||
}
|
||||
label={i18n('icu:BadgeDialog__become-a-sustainer-button')}
|
||||
onClick={() => setModalState(ModalState.BecomeSustainer)}
|
||||
/>
|
||||
</PanelSection>
|
||||
</>
|
||||
)}
|
||||
|
||||
{callHistoryGroup && (
|
||||
<CallHistoryGroupPanelSection
|
||||
callHistoryGroup={callHistoryGroup}
|
||||
|
@@ -9,12 +9,16 @@ import { bemGenerator } from './util';
|
||||
|
||||
export enum IconType {
|
||||
'approveAllMembers' = 'approveAllMembers',
|
||||
'bell' = 'bell',
|
||||
'block' = 'block',
|
||||
'edit' = 'edit',
|
||||
'unblock' = 'unblock',
|
||||
'color' = 'color',
|
||||
'down' = 'down',
|
||||
'forward' = 'forward',
|
||||
'heart' = 'heart',
|
||||
'help' = 'help',
|
||||
'invite' = 'invite',
|
||||
'invites' = 'invites',
|
||||
'leave' = 'leave',
|
||||
'link' = 'link',
|
||||
|
@@ -4943,6 +4943,7 @@ export class ConversationModel extends window.Backbone
|
||||
if (!messaging) {
|
||||
throw new Error('setProfileAvatar: Cannot fetch avatar when offline!');
|
||||
}
|
||||
|
||||
const avatar = await messaging.getAvatar(avatarUrl);
|
||||
|
||||
// decrypt
|
||||
|
@@ -119,6 +119,8 @@ export type GlobalModalsStateType = ReadonlyDeep<{
|
||||
gv2MigrationProps?: MigrateToGV2PropsType;
|
||||
hasConfirmationModal: boolean;
|
||||
isProfileEditorVisible: boolean;
|
||||
isProfileNameWarningModalVisible: boolean;
|
||||
profileNameWarningModalConversationType?: string;
|
||||
isShortcutGuideModalVisible: boolean;
|
||||
isSignalConnectionsVisible: boolean;
|
||||
isStoriesSettingsVisible: boolean;
|
||||
@@ -168,6 +170,8 @@ const TOGGLE_NOTE_PREVIEW_MODAL = 'globalModals/TOGGLE_NOTE_PREVIEW_MODAL';
|
||||
const TOGGLE_PROFILE_EDITOR = 'globalModals/TOGGLE_PROFILE_EDITOR';
|
||||
export const TOGGLE_PROFILE_EDITOR_ERROR =
|
||||
'globalModals/TOGGLE_PROFILE_EDITOR_ERROR';
|
||||
const TOGGLE_PROFILE_NAME_WARNING_MODAL =
|
||||
'globalModals/TOGGLE_PROFILE_NAME_WARNING_MODAL';
|
||||
const TOGGLE_SAFETY_NUMBER_MODAL = 'globalModals/TOGGLE_SAFETY_NUMBER_MODAL';
|
||||
const TOGGLE_ADD_USER_TO_ANOTHER_GROUP_MODAL =
|
||||
'globalModals/TOGGLE_ADD_USER_TO_ANOTHER_GROUP_MODAL';
|
||||
@@ -295,6 +299,13 @@ export type ToggleProfileEditorErrorActionType = ReadonlyDeep<{
|
||||
type: typeof TOGGLE_PROFILE_EDITOR_ERROR;
|
||||
}>;
|
||||
|
||||
export type ToggleProfileNameWarningModalActionType = ReadonlyDeep<{
|
||||
type: typeof TOGGLE_PROFILE_NAME_WARNING_MODAL;
|
||||
payload?: {
|
||||
conversationType: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
type ToggleSafetyNumberModalActionType = ReadonlyDeep<{
|
||||
type: typeof TOGGLE_SAFETY_NUMBER_MODAL;
|
||||
payload: string | undefined;
|
||||
@@ -474,6 +485,7 @@ export type GlobalModalsActionType = ReadonlyDeep<
|
||||
| ToggleNotePreviewModalActionType
|
||||
| ToggleProfileEditorActionType
|
||||
| ToggleProfileEditorErrorActionType
|
||||
| ToggleProfileNameWarningModalActionType
|
||||
| ToggleSafetyNumberModalActionType
|
||||
| ToggleSignalConnectionsModalActionType
|
||||
| ToggleUsernameOnboardingActionType
|
||||
@@ -523,6 +535,7 @@ export const actions = {
|
||||
toggleNotePreviewModal,
|
||||
toggleProfileEditor,
|
||||
toggleProfileEditorHasError,
|
||||
toggleProfileNameWarningModal,
|
||||
toggleSafetyNumberModal,
|
||||
toggleSignalConnectionsModal,
|
||||
toggleUsernameOnboarding,
|
||||
@@ -844,6 +857,15 @@ function toggleProfileEditorHasError(): ToggleProfileEditorErrorActionType {
|
||||
return { type: TOGGLE_PROFILE_EDITOR_ERROR };
|
||||
}
|
||||
|
||||
function toggleProfileNameWarningModal(
|
||||
conversationType?: string
|
||||
): ToggleProfileNameWarningModalActionType {
|
||||
return {
|
||||
type: TOGGLE_PROFILE_NAME_WARNING_MODAL,
|
||||
payload: conversationType ? { conversationType } : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function toggleSafetyNumberModal(
|
||||
safetyNumberModalContactId?: string
|
||||
): ToggleSafetyNumberModalActionType {
|
||||
@@ -1170,6 +1192,8 @@ export function getEmptyState(): GlobalModalsStateType {
|
||||
confirmLeaveCallModalState: null,
|
||||
editNicknameAndNoteModalProps: null,
|
||||
isProfileEditorVisible: false,
|
||||
isProfileNameWarningModalVisible: false,
|
||||
profileNameWarningModalConversationType: undefined,
|
||||
isShortcutGuideModalVisible: false,
|
||||
isSignalConnectionsVisible: false,
|
||||
isStoriesSettingsVisible: false,
|
||||
@@ -1222,6 +1246,16 @@ export function reducer(
|
||||
profileEditorHasError: !state.profileEditorHasError,
|
||||
};
|
||||
}
|
||||
if (action.type === TOGGLE_PROFILE_NAME_WARNING_MODAL) {
|
||||
return {
|
||||
...state,
|
||||
isProfileNameWarningModalVisible: !state.isProfileNameWarningModalVisible,
|
||||
profileNameWarningModalConversationType:
|
||||
state.isProfileNameWarningModalVisible
|
||||
? undefined
|
||||
: action.payload?.conversationType,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === SHOW_WHATS_NEW_MODAL) {
|
||||
return {
|
||||
|
@@ -7,9 +7,28 @@ import { isSignalConnection } from '../../util/getSignalConnections';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { getGlobalModalsState } from '../selectors/globalModals';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
import type { ConversationType } from '../ducks/conversations';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
||||
|
||||
function isFromOrAddedByTrustedContact(
|
||||
conversation: ConversationType
|
||||
): boolean {
|
||||
if (conversation.type === 'direct') {
|
||||
return Boolean(conversation.name) || Boolean(conversation.profileSharing);
|
||||
}
|
||||
|
||||
const addedByConv = getAddedByForOurPendingInvitation(conversation);
|
||||
if (!addedByConv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
addedByConv.isMe || addedByConv.name || addedByConv.profileSharing
|
||||
);
|
||||
}
|
||||
|
||||
export const SmartAboutContactModal = memo(function SmartAboutContactModal() {
|
||||
const i18n = useSelector(getIntl);
|
||||
@@ -27,6 +46,7 @@ export const SmartAboutContactModal = memo(function SmartAboutContactModal() {
|
||||
toggleSignalConnectionsModal,
|
||||
toggleSafetyNumberModal,
|
||||
toggleNotePreviewModal,
|
||||
toggleProfileNameWarningModal,
|
||||
} = useGlobalModalActions();
|
||||
|
||||
const handleOpenNotePreviewModal = useCallback(() => {
|
||||
@@ -47,8 +67,10 @@ export const SmartAboutContactModal = memo(function SmartAboutContactModal() {
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
toggleSafetyNumberModal={toggleSafetyNumberModal}
|
||||
isSignalConnection={isSignalConnection(conversation)}
|
||||
fromOrAddedByTrustedContact={isFromOrAddedByTrustedContact(conversation)}
|
||||
onClose={toggleAboutContactModal}
|
||||
onOpenNotePreviewModal={handleOpenNotePreviewModal}
|
||||
toggleProfileNameWarningModal={toggleProfileNameWarningModal}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@@ -336,6 +336,7 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
|
||||
blockConversation={blockConversation}
|
||||
reportSpam={reportSpam}
|
||||
deleteConversation={deleteConversation}
|
||||
sharedGroupNames={conversation.sharedGroupNames}
|
||||
// Signal Conversation
|
||||
isSignalConversation={isSignalConversation(conversation)}
|
||||
isMuted={isConversationMuted(conversation)}
|
||||
|
@@ -31,6 +31,7 @@ import { SmartCallLinkAddNameModal } from './CallLinkAddNameModal';
|
||||
import { SmartConfirmLeaveCallModal } from './ConfirmLeaveCallModal';
|
||||
import { SmartCallLinkPendingParticipantModal } from './CallLinkPendingParticipantModal';
|
||||
import { SmartAttachmentNotAvailableModal } from './AttachmentNotAvailableModal';
|
||||
import { SmartProfileNameWarningModal } from './ProfileNameWarningModal';
|
||||
|
||||
function renderCallLinkAddNameModal(): JSX.Element {
|
||||
return <SmartCallLinkAddNameModal />;
|
||||
@@ -60,6 +61,10 @@ function renderProfileEditor(): JSX.Element {
|
||||
return <SmartProfileEditorModal />;
|
||||
}
|
||||
|
||||
function renderProfileNameWarningModal(): JSX.Element {
|
||||
return <SmartProfileNameWarningModal />;
|
||||
}
|
||||
|
||||
function renderUsernameOnboarding(): JSX.Element {
|
||||
return <SmartUsernameOnboardingModal />;
|
||||
}
|
||||
@@ -130,6 +135,8 @@ export const SmartGlobalModalContainer = memo(
|
||||
messageRequestActionsConfirmationProps,
|
||||
notePreviewModalProps,
|
||||
isProfileEditorVisible,
|
||||
isProfileNameWarningModalVisible,
|
||||
profileNameWarningModalConversationType,
|
||||
isShortcutGuideModalVisible,
|
||||
isSignalConnectionsVisible,
|
||||
isStoriesSettingsVisible,
|
||||
@@ -229,6 +236,7 @@ export const SmartGlobalModalContainer = memo(
|
||||
i18n={i18n}
|
||||
isAboutContactModalVisible={aboutContactModalContactId != null}
|
||||
isProfileEditorVisible={isProfileEditorVisible}
|
||||
isProfileNameWarningModalVisible={isProfileNameWarningModalVisible}
|
||||
isShortcutGuideModalVisible={isShortcutGuideModalVisible}
|
||||
isSignalConnectionsVisible={isSignalConnectionsVisible}
|
||||
isStoriesSettingsVisible={isStoriesSettingsVisible}
|
||||
@@ -253,6 +261,7 @@ export const SmartGlobalModalContainer = memo(
|
||||
}
|
||||
renderNotePreviewModal={renderNotePreviewModal}
|
||||
renderProfileEditor={renderProfileEditor}
|
||||
renderProfileNameWarningModal={renderProfileNameWarningModal}
|
||||
renderUsernameOnboarding={renderUsernameOnboarding}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
renderSendAnywayDialog={renderSendAnywayDialog}
|
||||
@@ -267,6 +276,9 @@ export const SmartGlobalModalContainer = memo(
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
userNotFoundModalState={userNotFoundModalState}
|
||||
usernameOnboardingState={usernameOnboardingState}
|
||||
profileNameWarningModalConversationType={
|
||||
profileNameWarningModalConversationType
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -1,21 +1,43 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { PanelType } from '../../types/Panels';
|
||||
import { ConversationHero } from '../../components/conversation/ConversationHero';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||
import { isSignalConversation } from '../../util/isSignalConversation';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import {
|
||||
type ConversationType,
|
||||
useConversationsActions,
|
||||
} from '../ducks/conversations';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import { useStoriesActions } from '../ducks/stories';
|
||||
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
|
||||
|
||||
type SmartHeroRowProps = Readonly<{
|
||||
id: string;
|
||||
}>;
|
||||
|
||||
function isFromOrAddedByTrustedContact(
|
||||
conversation: ConversationType
|
||||
): boolean {
|
||||
if (conversation.type === 'direct') {
|
||||
return Boolean(conversation.name) || Boolean(conversation.profileSharing);
|
||||
}
|
||||
|
||||
const addedByConv = getAddedByForOurPendingInvitation(conversation);
|
||||
if (!addedByConv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
addedByConv.isMe || addedByConv.name || addedByConv.profileSharing
|
||||
);
|
||||
}
|
||||
|
||||
export const SmartHeroRow = memo(function SmartHeroRow({
|
||||
id,
|
||||
}: SmartHeroRowProps) {
|
||||
@@ -31,8 +53,15 @@ export const SmartHeroRow = memo(function SmartHeroRow({
|
||||
const badge = getPreferredBadge(conversation.badges);
|
||||
const hasStories = hasStoriesSelector(id);
|
||||
const isSignalConversationValue = isSignalConversation(conversation);
|
||||
const { unblurAvatar, updateSharedGroups } = useConversationsActions();
|
||||
const { toggleAboutContactModal } = useGlobalModalActions();
|
||||
const fromOrAddedByTrustedContact =
|
||||
isFromOrAddedByTrustedContact(conversation);
|
||||
const { pushPanelForConversation, unblurAvatar, updateSharedGroups } =
|
||||
useConversationsActions();
|
||||
const { toggleAboutContactModal, toggleProfileNameWarningModal } =
|
||||
useGlobalModalActions();
|
||||
const openConversationDetails = useCallback(() => {
|
||||
pushPanelForConversation({ type: PanelType.ConversationDetails });
|
||||
}, [pushPanelForConversation]);
|
||||
const { viewUserStories } = useStoriesActions();
|
||||
const {
|
||||
about,
|
||||
@@ -41,6 +70,8 @@ export const SmartHeroRow = memo(function SmartHeroRow({
|
||||
groupDescription,
|
||||
isMe,
|
||||
membersCount,
|
||||
nicknameGivenName,
|
||||
nicknameFamilyName,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
sharedGroupNames,
|
||||
@@ -48,6 +79,10 @@ export const SmartHeroRow = memo(function SmartHeroRow({
|
||||
type,
|
||||
unblurredAvatarUrl,
|
||||
} = conversation;
|
||||
|
||||
const isDirectConvoAndHasNickname =
|
||||
type === 'direct' && Boolean(nicknameGivenName || nicknameFamilyName);
|
||||
|
||||
return (
|
||||
<ConversationHero
|
||||
about={about}
|
||||
@@ -55,19 +90,23 @@ export const SmartHeroRow = memo(function SmartHeroRow({
|
||||
avatarUrl={avatarUrl}
|
||||
badge={badge}
|
||||
conversationType={type}
|
||||
fromOrAddedByTrustedContact={fromOrAddedByTrustedContact}
|
||||
groupDescription={groupDescription}
|
||||
hasStories={hasStories}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
isDirectConvoAndHasNickname={isDirectConvoAndHasNickname}
|
||||
isMe={isMe}
|
||||
isSignalConversation={isSignalConversationValue}
|
||||
membersCount={membersCount}
|
||||
openConversationDetails={openConversationDetails}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
theme={theme}
|
||||
title={title}
|
||||
toggleAboutContactModal={toggleAboutContactModal}
|
||||
toggleProfileNameWarningModal={toggleProfileNameWarningModal}
|
||||
unblurAvatar={unblurAvatar}
|
||||
unblurredAvatarUrl={unblurredAvatarUrl}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
|
34
ts/state/smart/ProfileNameWarningModal.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { getGlobalModalsState } from '../selectors/globalModals';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
import { ProfileNameWarningModal } from '../../components/conversation/ProfileNameWarningModal';
|
||||
|
||||
export const SmartProfileNameWarningModal = memo(
|
||||
function SmartProfileNameWarningModal() {
|
||||
const i18n = useSelector(getIntl);
|
||||
const globalModals = useSelector(getGlobalModalsState);
|
||||
const { profileNameWarningModalConversationType } = globalModals;
|
||||
const { toggleProfileNameWarningModal } = useGlobalModalActions();
|
||||
|
||||
if (
|
||||
!profileNameWarningModalConversationType ||
|
||||
(profileNameWarningModalConversationType !== 'group' &&
|
||||
profileNameWarningModalConversationType !== 'direct')
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ProfileNameWarningModal
|
||||
conversationType={profileNameWarningModalConversationType}
|
||||
i18n={i18n}
|
||||
onClose={toggleProfileNameWarningModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
@@ -281,10 +281,22 @@ export async function pinContact(
|
||||
await phone.setStorageState(state);
|
||||
}
|
||||
|
||||
export function acceptConversation(page: Page): Promise<void> {
|
||||
return page
|
||||
export async function acceptConversation(page: Page): Promise<void> {
|
||||
await page
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
|
||||
const confirmationButton = page
|
||||
.locator('.MessageRequestActionsConfirmation')
|
||||
.getByRole('button', { name: 'Accept' });
|
||||
|
||||
await confirmationButton.waitFor({
|
||||
timeout: 500,
|
||||
});
|
||||
|
||||
if (await confirmationButton.isVisible()) {
|
||||
await confirmationButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimeline(page: Page): Locator {
|
||||
|
@@ -10,6 +10,7 @@ import assert from 'assert';
|
||||
import * as durations from '../../util/durations';
|
||||
import type { App } from '../playwright';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import { acceptConversation } from '../helpers';
|
||||
|
||||
export const debug = createDebug('mock:test:edit');
|
||||
|
||||
@@ -68,7 +69,7 @@ describe('unknown contacts', function (this: Mocha.Suite) {
|
||||
|
||||
debug('accepting message request');
|
||||
await page.getByText('message you and share your name').waitFor();
|
||||
await page.getByRole('button', { name: 'Accept' }).click();
|
||||
await acceptConversation(page);
|
||||
await page.getByText('message you and share your name').waitFor({
|
||||
state: 'detached',
|
||||
});
|
||||
|
@@ -13,7 +13,7 @@ import {
|
||||
} from '../../util/libphonenumberInstance';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import { expectSystemMessages } from '../helpers';
|
||||
import { acceptConversation, expectSystemMessages } from '../helpers';
|
||||
|
||||
export const debug = createDebug('mock:test:gv2');
|
||||
|
||||
@@ -103,9 +103,7 @@ describe('pnp/accept gv2 invite', function (this: Mocha.Suite) {
|
||||
const conversationStack = window.locator('.Inbox__conversation-stack');
|
||||
|
||||
debug('Accepting');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
group = await phone.waitForGroupUpdate(group);
|
||||
assert.strictEqual(group.revision, 2);
|
||||
@@ -256,9 +254,7 @@ describe('pnp/accept gv2 invite', function (this: Mocha.Suite) {
|
||||
.waitFor();
|
||||
|
||||
debug('Accepting');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Checking final notification');
|
||||
await window
|
||||
|
@@ -24,6 +24,7 @@ import {
|
||||
} from '../../types/Receipt';
|
||||
import { sleep } from '../../util/sleep';
|
||||
import {
|
||||
acceptConversation,
|
||||
expectSystemMessages,
|
||||
typeIntoInput,
|
||||
waitForEnabledComposer,
|
||||
@@ -85,7 +86,6 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||
const window = await app.getWindow();
|
||||
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const conversationStack = window.locator('.Inbox__conversation-stack');
|
||||
|
||||
debug('creating a stranger');
|
||||
const stranger = await server.createPrimaryDevice({
|
||||
@@ -137,9 +137,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from a stranger');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Wait for a pniSignatureMessage');
|
||||
{
|
||||
|
@@ -10,7 +10,11 @@ import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import { ReceiptType } from '../../types/Receipt';
|
||||
import { toUntaggedPni } from '../../types/ServiceId';
|
||||
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||
import {
|
||||
acceptConversation,
|
||||
typeIntoInput,
|
||||
waitForEnabledComposer,
|
||||
} from '../helpers';
|
||||
|
||||
export const debug = createDebug('mock:test:challenge:receipts');
|
||||
|
||||
@@ -106,7 +110,6 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
|
||||
const window = await app.getWindow();
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const conversationStack = window.locator('.Inbox__conversation-stack');
|
||||
|
||||
debug(`Opening conversation with contact (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
@@ -114,9 +117,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from contact - does not trigger captcha!');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Sending a message back to user - will trigger captcha!');
|
||||
{
|
||||
@@ -164,7 +165,6 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
|
||||
const window = await app.getWindow();
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const conversationStack = window.locator('.Inbox__conversation-stack');
|
||||
|
||||
debug('Sending a message from ContactA');
|
||||
const timestampA = bootstrap.getTimestamp();
|
||||
@@ -178,9 +178,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from ContactA - does not trigger captcha!');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Sending a message from ContactB');
|
||||
const timestampB = bootstrap.getTimestamp();
|
||||
@@ -194,9 +192,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from ContactB - does not trigger captcha!');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Sending a message back to ContactB - will trigger captcha!');
|
||||
{
|
||||
@@ -276,7 +272,6 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
|
||||
const window = await app.getWindow();
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const conversationStack = window.locator('.Inbox__conversation-stack');
|
||||
|
||||
debug(`Opening conversation with contact (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
@@ -284,9 +279,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from contact - does not trigger captcha!');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug('Sending a message back to user - will trigger captcha!');
|
||||
{
|
||||
@@ -355,9 +348,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||
.click();
|
||||
|
||||
debug('Accept conversation from Contact B - does not trigger captcha!');
|
||||
await conversationStack
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
await acceptConversation(window);
|
||||
|
||||
debug(
|
||||
'Sending to Contact B - we should not pop captcha because we are waiting!'
|
||||
|
@@ -81,6 +81,11 @@ describe('storage service', function (this: Mocha.Suite) {
|
||||
.locator('.module-message-request-actions button >> "Accept"')
|
||||
.click();
|
||||
|
||||
await window
|
||||
.locator('.MessageRequestActionsConfirmation')
|
||||
.getByRole('button', { name: 'Accept' })
|
||||
.click();
|
||||
|
||||
debug('Verify that storage state was updated');
|
||||
{
|
||||
const nextState = await phone.waitForStorageState({
|
||||
|
26
ts/util/getLocalizedUrl.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { mapToSupportLocale } from './mapToSupportLocale';
|
||||
|
||||
/**
|
||||
* Ensures the provided string contains "LOCALE".
|
||||
* If not, produces a readable TypeScript error.
|
||||
*/
|
||||
type RequiresLocale<T extends string> = T extends `${string}LOCALE${string}`
|
||||
? T
|
||||
: `Error: The URL must contain "LOCALE" but got "${T}"`;
|
||||
|
||||
/**
|
||||
* Replaces "LOCALE" in a URL with the appropriate localized support locale.
|
||||
*
|
||||
* @param url The URL string containing "LOCALE" to be replaced
|
||||
* @returns The URL with "LOCALE" replaced with the appropriate locale
|
||||
*/
|
||||
export function getLocalizedUrl<T extends string>(
|
||||
url: RequiresLocale<T>
|
||||
): string {
|
||||
const locale = window.SignalContext.getResolvedMessagesLocale();
|
||||
const supportLocale = mapToSupportLocale(locale);
|
||||
return url.replace('LOCALE', supportLocale);
|
||||
}
|