Frontend for multiple reactions (v1) (#18808)
* Remove extraneous comment (see 9361d2 and 5c18f8) * Flexible, multiple reaction types * Fix reaction counts * Re-use svg for active state for now * Update yml, update spec * Possible temp fix for failing tests * Colorize reaction icon svgs * Reaction engagement above post title * Index reactions engagements (for logged-out) * Maybe readinglist is special * Try using crayons' dropdown as a drawer? * readinglist isn't really public now * feat: update the styles for the reaction drawer * Grey background highlight, turn off border/shadow * Read our feature flag docs, saw this was recommended * Missed fifth emoji: party/tada * Fix JS test errors * Update test with tada * Suppress flashing engagements when no public reactions * Liberate jump-to-comments from unicorn replacement * 'Add reaction' on tooltip * Don't show reaction emoji on index unless it's been used * rubocop * Update heart+ total count when toggling * Do not include 'readinglist' in drawer/public counts * Fix semi-public readinglist so that icon is badged for current user * Tweak heart-plus svg * Style tweak: border on active reaction * Show reacted icon on drawer trigger for 1.5 sec * Tweak styles for engagements bar * Style tweaks for multiple engagements (#index) * Trying to get size working through crayons/inline_svg * Sparkle hearts * Restore unicorn * Make heart-plus-active work when user has an active reaction * Try 'hoverdown' a dropdown that activates with hover * Long touch? * Tap *outside* the drawer to close * Mobile reaction drawer is also supposed to be columns * More reaction count cleanup * Final emoticons maybe? * Fix reaction bug when feature disabled * Remove readinglist from public reaction counts * Update specs for new reaction categories * Shuffle makes specs flaky * Order does not matter * rubocop * Update to preserve readinglist analytics * Shuffle makes specs flaky * Fix flickering images, remove icon highlight for now * Don't update total for readinglist * reactions_by_user_id * Try renaming this observer function * Try unid ids for SVGs * Remove local test file * Simplistic test for the unique svg transform * Fix javascript for current SVG * Signifcant string literals in this case, rubocop * Use the right expected output Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com>
|
|
@ -4,3 +4,5 @@
|
|||
//= link_directory ../stylesheets .css
|
||||
//= link s3_direct_upload.js
|
||||
//= link katex.css
|
||||
//= link icon.png
|
||||
//= link loading-ellipsis.svg
|
||||
|
|
|
|||
46
app/assets/images/exploding-head.svg
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.412 8.604a2.047 2.047 0 0 0-.321-.028 4.007 4.007 0 0 0-3.987 4.416C2.32 18.278 6.662 22.5 12 22.5c5.477 0 9.917-4.44 9.917-9.916 0-.018-.002-.036-.004-.053-.001-.012-.003-.024-.003-.037l.003-.053c.002-.026.004-.053.004-.08a4.007 4.007 0 0 0-4.007-4.006c-.119 0-.257.014-.401.037a11.12 11.12 0 0 0-.347.578c.216-.026.391-.011.513.058.167.094.062.22-.036.335-.115.136-.219.259.15.302.347.04.53-.03.696-.094.166-.064.313-.121.588-.05.7.181.054.808-.357 1.143-.593.404-.903.245-1.262.06-.229-.117-.477-.245-.832-.245-.07 0-.132.006-.19.014l-.002.002c-.263.036-.425.148-.65.304-.188.128-.418.287-.785.457-.335.155-.757-.037-1.149-.215-.26-.119-.505-.23-.703-.23-.255 0-.503.13-.76.266-.42.221-.866.456-1.41.16a4.177 4.177 0 0 1-.405-.266c-.326-.232-.653-.464-1.125-.452a2.499 2.499 0 0 0-1.06.244c-.226.098-.426.185-.766.174v-.002a2.33 2.33 0 0 1-.304-.03c-1.045-.175-1.66-.431-2.021-.676-.364-.324-.325-.604-.137-.745.146-.11.65-.298 1.001.03.236.22.402.132.633.01l.135-.069a12.54 12.54 0 0 0-.512-.85Z" fill="url(#a)"/>
|
||||
<path d="M6.924 9.453c-.047.023-.092.047-.135.07-.23.121-.397.209-.633-.011-.352-.328-.855-.139-1-.03-.189.141-.228.421.136.745.362.245.976.501 2.02.676.116.02.213.027.305.03-.135-.355-.362-.88-.692-1.479ZM16.431 10.492c.059-.008.12-.014.191-.014.355 0 .603.128.832.245.36.185.669.344 1.262-.06.411-.335 1.056-.962.357-1.143-.275-.071-.422-.014-.588.05-.165.064-.349.135-.696.094-.369-.043-.265-.166-.15-.302.098-.116.203-.24.036-.335-.122-.07-.297-.084-.513-.058l-.001.003a13.82 13.82 0 0 0-.73 1.52Z" fill="#F19020"/>
|
||||
<path d="M8.5 15.074c.644 0 1.167-.696 1.167-1.555 0-.86-.523-1.556-1.167-1.556-.645 0-1.167.696-1.167 1.556 0 .859.522 1.555 1.167 1.555Z" fill="url(#b)"/>
|
||||
<path d="M15.5 15.074c.645 0 1.167-.696 1.167-1.555s-.522-1.556-1.167-1.556c-.644 0-1.167.697-1.167 1.556 0 .859.522 1.555 1.167 1.555Z" fill="url(#c)"/>
|
||||
<path d="M14.527 18.206c0 1.595-1.131 2.887-2.527 2.887-1.396 0-2.527-1.292-2.527-2.887s1.131-2.888 2.527-2.888c1.396 0 2.527 1.293 2.527 2.888Z" fill="url(#d)"/>
|
||||
<path d="M5.589 7.491c.088-.034.173-.074.255-.12a1.302 1.302 0 0 0 1.03.241 2.815 2.815 0 0 0 2.398 1.344 2.83 2.83 0 0 0 2.376-1.293c.42.514 1.05.85 1.767.85 1.058 0 1.94-.723 2.202-1.699.01 0 .019.002.027.003.01.002.02.003.031.003.165 0 .326-.018.482-.05.381.324.87.527 1.41.527.347 0 .673-.083.963-.228-.465.48-.971 1.239-.971 1.239-.72-.046-1.43-.31-1.43-.31-.911 1.504-2.953 1.813-4.411.938-2.097 1.768-4.872.366-4.872.366S6.16 8.136 5.59 7.492Z" fill="#CE5C1F"/>
|
||||
<path d="M6.925 9.457a12.72 12.72 0 0 0-.262-.453c.114.182.183.3.183.3s2.775 1.403 4.872-.365c1.458.875 3.5.565 4.41-.939 0 0 .71.264 1.43.31-.192.278-.238.378-.397.664-.298.533-.543 1.06-.73 1.522-.264.036-.426.148-.652.303a4.897 4.897 0 0 1-.784.458c-.335.155-.757-.037-1.149-.216-.26-.118-.505-.23-.703-.23-.255 0-.503.13-.76.267-.42.221-.866.456-1.41.159a4.157 4.157 0 0 1-.405-.265c-.326-.232-.653-.465-1.125-.452-.531.014-.811.135-1.06.244-.226.098-.426.185-.766.174-.134-.356-.362-.88-.692-1.481Z" fill="url(#e)"/>
|
||||
<path d="M11.834 3.682a1.257 1.257 0 0 1 .184 2.503 1.312 1.312 0 0 1-1.277 1.02 1.3 1.3 0 0 1-.634-.17 1.307 1.307 0 0 1-2.41.37 2.181 2.181 0 0 1-1.603-1.212 1.174 1.174 0 1 1-.146-1.471 2.183 2.183 0 0 1 2.115-1.656c.575 0 1.095.228 1.486.591a.97.97 0 0 1 1.694.179 1.25 1.25 0 0 1 .591-.154Z" fill="url(#f)"/>
|
||||
<path d="M18.951 4.864a1.237 1.237 0 1 1-2.474 0 1.237 1.237 0 0 1 2.474 0Z" fill="url(#g)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.256 3.737c.128-.038.26-.064.4-.064a1.437 1.437 0 0 1 0 2.874c-.14 0-.272-.027-.4-.064-.4.493-1.004.814-1.69.814-.54 0-1.028-.203-1.41-.527-.155.033-.316.05-.481.05a.207.207 0 0 1-.03-.002.232.232 0 0 0-.028-.003 2.283 2.283 0 0 1-2.202 1.698 2.275 2.275 0 0 1-1.767-.85 2.83 2.83 0 0 1-2.376 1.293 2.815 2.815 0 0 1-2.397-1.343 1.267 1.267 0 0 1-1.03-.241c-.28.155-.6.248-.942.248a1.969 1.969 0 1 1 0-3.938c.124 0 .245.015.364.037a2.178 2.178 0 0 1 1.92-1.162c.113 0 .22.017.327.034a2.183 2.183 0 0 1 1.837-1.005c.957 0 1.762.62 2.059 1.477a1.304 1.304 0 0 1 2.23.287 2.328 2.328 0 0 1 2.034-1.205c.696 0 1.315.31 1.743.793l.061-.008c.029-.004.058-.008.088-.008.685 0 1.289.322 1.69.815Zm-6.162 1.206a1.26 1.26 0 0 0-1.26-1.26 1.25 1.25 0 0 0-.59.153.971.971 0 0 0-1.695-.178c-.391-.364-.91-.591-1.486-.591a2.183 2.183 0 0 0-2.115 1.655 1.174 1.174 0 1 0 .146 1.471c.301.631.892 1.093 1.603 1.213a1.307 1.307 0 0 0 2.41-.37 1.3 1.3 0 0 0 .634.17c.624 0 1.145-.437 1.278-1.02a1.257 1.257 0 0 0 1.075-1.243Zm4.62 1.158a1.237 1.237 0 1 0 0-2.474 1.237 1.237 0 0 0 0 2.474Z" fill="url(#h)"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="12" y1="14.155" x2="12" y2="22.499" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC49"/>
|
||||
<stop offset="1" stop-color="#FFB649"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="12" y1="11.962" x2="12" y2="15.074" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#65471B"/>
|
||||
<stop offset="1" stop-color="#4E340F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="12" y1="11.963" x2="12" y2="15.075" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#65471B"/>
|
||||
<stop offset="1" stop-color="#4E340F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="12" y1="11.964" x2="12" y2="15.076" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#65471B"/>
|
||||
<stop offset="1" stop-color="#4E340F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="e" x1="11.891" y1="6.818" x2="11.891" y2="11.376" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFA271"/>
|
||||
<stop offset="1" stop-color="#F36C24"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f" x1="8.518" y1="3.066" x2="8.518" y2="8.062" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EFEFEF"/>
|
||||
<stop offset="1" stop-color="#E6E6E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="g" x1="8.518" y1="3.067" x2="8.518" y2="8.063" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EFEFEF"/>
|
||||
<stop offset="1" stop-color="#E6E6E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="h" x1="12.014" y1="1.586" x2="12.014" y2="8.956" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D8D8D8"/>
|
||||
<stop offset="1" stop-color="#C6C6C6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
14
app/assets/images/fire.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<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="M21.31 9.161a9.893 9.893 0 0 1 .607 3.423c0 4.982-3.673 9.105-8.459 9.81a9.888 9.888 0 0 0 5.51-2.757 6.963 6.963 0 0 0-.905-4.136c.087 1.798-1.494 2.504-2.344 2.163-.91-.364-.965-.923-1.061-1.9-.034-.34-.072-.733-.154-1.186-.316-1.75-1.532-2.965-3.33-3.5 1.313 3.694-.727 5.055-1.797 5.299-1.092.248-2.189 0-2.314-2.338a6.98 6.98 0 0 0-2.03 5.598 9.89 9.89 0 0 0 5.524 2.76c-4.793-.699-8.474-4.826-8.474-9.813A9.88 9.88 0 0 1 4.44 6.168c-.849 3.087.072 5.024 1.728 4.86C7.65 10.883 8.282 9.57 7.674 7.99c-.583-1.515-.528-3.858 1.36-5.25-.582 2.625.998 4.243 2.723 4.108 1.556-.121 3.038-2.114 1.361-5.323 4.036 2.115 4.181 4.658 4.043 6.88-.02.315-.098.712-.183 1.144-.27 1.368-.61 3.083.637 3.619 1.75.752 3.427-.863 3.695-4.007Z" fill="url(#a)"/>
|
||||
<path d="M18.063 15.499c.087 1.799-1.494 2.504-2.344 2.164-1.229-.492-.9-1.337-1.215-3.087-.316-1.75-1.532-2.965-3.33-3.5 1.313 3.694-.727 5.056-1.797 5.3-1.092.248-2.189-.001-2.314-2.338a6.979 6.979 0 0 0-2.03 5.597A9.88 9.88 0 0 0 12 22.5a9.88 9.88 0 0 0 6.968-2.865 6.963 6.963 0 0 0-.905-4.136Z" fill="url(#b)"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="12" y1="1.525" x2="12" y2="22.501" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFA271"/>
|
||||
<stop offset="1" stop-color="#F36C24"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="12" y1="15.761" x2="12" y2="22.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC49"/>
|
||||
<stop offset="1" stop-color="#FFB649"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
11
app/assets/images/heart-plus-active.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1000_2078)">
|
||||
<path d="M19 14V17H22V19H19V22H17V19H14V17H17V14H19ZM20.243 4.75698C22.403 6.92298 22.572 10.314 20.75 12.667C19.926 12.24 18.99 12 18 12C14.686 12 12 14.686 12 18C12 19.009 12.249 19.96 12.689 20.794L11.999 21.485L3.52101 12.993C1.41701 10.637 1.49601 7.01898 3.75701 4.75698C6.02201 2.49298 9.64501 2.41698 12.001 4.52898C14.35 2.41998 17.98 2.48998 20.243 4.75698Z" fill="#EF4444"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0765 16.9736V13.9736H17.0765V16.9736H14.0765V18.9736H17.0765V21.9736H19.0765V18.9736H22.0765V16.9736H19.0765ZM20.8271 12.6399C20.1623 12.2963 19.4251 12.0735 18.6444 12.0002C19.4244 12.0734 20.1619 12.2962 20.8265 12.6406C20.8267 12.6404 20.8269 12.6401 20.8271 12.6399ZM12.7654 20.7677C12.6672 20.5815 12.5785 20.3894 12.5 20.1922C12.5786 20.3894 12.6673 20.5814 12.7655 20.7676L12.7654 20.7677Z" fill="#404040"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1000_2078">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
3
app/assets/images/heart-plus.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 11V14H20V16H16.999L17 19H15L14.999 16H12V14H15V11H17ZM18.243 1.75698C20.505 4.02498 20.583 7.63698 18.479 9.99198L17.059 8.57398C18.39 7.04998 18.32 4.65998 16.827 3.16998C15.324 1.67098 12.907 1.60698 11.337 3.01698L10.002 4.21498L8.66603 3.01798C7.09103 1.60598 4.67503 1.66798 3.17203 3.17198C1.68203 4.66198 1.60703 7.04698 2.98003 8.62298L11.412 17.069L10 18.485L1.52003 9.99298C-0.583971 7.63698 -0.50497 4.01898 1.75603 1.75698C4.02103 -0.50702 7.64403 -0.58302 10 1.52898C12.349 -0.58002 15.979 -0.51002 18.242 1.75698H18.243Z" fill="#525252"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
0
app/assets/images/lightbulb.svg
Normal file
60
app/assets/images/multi-unicorn.svg
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.178 11.2a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#a)"/>
|
||||
<path d="M17.518 17.126c-.07.384.106.737.391.788.286.052.574-.216.644-.6.07-.385-.106-.738-.391-.79-.286-.052-.574.217-.644.602Z" fill="url(#b)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m18.394 14.92-.214-.461c-1.296.094-2.863.67-3.127 1.992-.112.557-.469 1.028-.924 1.413.028.06.053.117.078.174.113.256.218.492.5.788 1.334 1.4 3.325.74 4.093-.619.62-1.098.196-2.003-.406-3.288Zm-.485 2.996c-.285-.052-.46-.405-.39-.789.069-.385.357-.653.643-.602.285.052.46.405.391.79-.07.383-.358.652-.644.6Z" fill="url(#c)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m14.475 6.186.002.001c-.298.265-.288.796.038 1.213.322.412.825.556 1.155.345l-.005.01.027-.023a.112.112 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.003c.178.195.335.395.46.598h.002c.648 1.22.82 2.491.98 3.66.12.893.234 1.727.545 2.433.122.277.243.536.358.782-.07-.148-.141-.301-.214-.46-1.296.094-2.863.67-3.127 1.991-.112.558-.469 1.029-.924 1.414-.17-.364-.433-.795-1.25-1.49-1.588-.317-2.563-1.045-2.563-2.552 0 0-.464-.223-1.065 3.505-.156.965-.098 1.93-.036 2.934l.017.294c.063 1.057-.796 1.895-1.855 1.895H3.521a1.969 1.969 0 0 1-1.969-1.969v-7.45 3.902c.464-1.023 1-2.468 1.464-3.714.371-.998.694-1.868.894-2.29 1.643-3.476 4.557-6.5 9.001-5.482a7.492 7.492 0 0 1 1.565.739Zm-1.297 5.012a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#d)"/>
|
||||
<path d="m16.686 4.673.034-.064.031-.057c.422-.782-.161-.511-.532-.255-.178.124-.855.382-1.522.621.24.148.49.35.75.57.35-.237.775-.516 1.239-.815Z" fill="url(#e)"/>
|
||||
<path d="M13.878 3.958c.292-.127.524-.227.63-.374.678-.928.487.27.415.617a3.887 3.887 0 0 1-.225.717 5.45 5.45 0 0 1-.597 1.05 7.02 7.02 0 0 0-1.19-.522 8.125 8.125 0 0 0-.804-.143c.534-.81 1.249-1.12 1.771-1.345Z" fill="url(#f)"/>
|
||||
<path d="M16.049 7.444c.178.195.335.396.46.599 0 0 1.298.328 1.971-.786.067-.11-.043-.236-.17-.222-.458.052-.9-.097-1.322-.344-.5.406-.852.687-.94.753Z" fill="url(#g)"/>
|
||||
<path d="M15.447 5.487c-.469.316-.805.554-.93.66-.003.003-.006.004-.01.006l-.009.005-.005.007c-.002.003-.003.006-.006.008l-.009.008-.002.003a8.712 8.712 0 0 0-.375-.217 5.44 5.44 0 0 0 .596-1.049c.24.147.49.349.75.57Z" fill="url(#h)"/>
|
||||
<path d="M13.263 4.25a3.192 3.192 0 0 0-1.156 1.053c-3.995-.5-6.655 2.363-8.197 5.625-.2.422-.523 1.292-.894 2.29-.463 1.245-1 2.69-1.463 3.714V9.779c0-1.26.24-2.596 1.032-3.577a5.7 5.7 0 0 1 .96-.933c.117-.09.282.035.244.178-.213.786-.317 1.869-.195 2.64.087-2.288 1.86-3.839 2.552-4.196 1.208-.623 2.4-.58 3.715-.471.113.01.184.142.146.25-.06.171-.11.413-.081.673.28-.51.935-.828 1.36-.741.77.156 1.45.38 1.977.648Z" fill="url(#i)"/>
|
||||
<path d="M13.646 5.738c.152-.238.27-.483.37-.693.118-.247.214-.446.316-.532.202-.168-.068-.17-.068-.17-.44.265-.972.662-1.278 1.129.21.07.432.16.66.266Z" fill="url(#j)"/>
|
||||
<path d="M14.475 6.187h.002c-.298.266-.288.796.038 1.213.322.412.825.556 1.155.346l-.005.01.027-.023a.115.115 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.004c.11-.084.654-.517 1.38-1.113a2.647 2.647 0 0 1-.634-.613 2.576 2.576 0 0 1-.428-.838l.528-.342c.014.173.1.473.368.831.24.323.479.494.642.57.35-.29.728-.605 1.11-.929a2.775 2.775 0 0 1-.518-.531 2.71 2.71 0 0 1-.385-.707c.17-.106.34-.21.511-.314.044.174.144.408.342.672.18.243.357.397.503.492.36-.309.717-.62 1.05-.92a2.92 2.92 0 0 1-.694-.942l-.005-.014c.176-.101.347-.199.512-.291l.01.027.014.037c.065.141.15.282.25.417.097.131.204.248.316.347l.017.014a.21.21 0 0 1 .015.011c.952-.877 1.619-1.586 1.472-1.733-.417-.416-7.235 4.002-7.909 4.573l-.008.005-.01.006-.005.007-.006.007-.009.008-.003.005Z" fill="url(#k)"/>
|
||||
<path d="m19.824 2.761-.005-.014c.176-.102.347-.2.512-.292.01.022.013.04.024.064.065.142.15.283.25.418.097.13.204.247.316.347l.033.025-.435.394a2.92 2.92 0 0 1-.695-.942ZM18.497 4.48a2.71 2.71 0 0 1-.385-.707l.511-.314c.044.174.144.408.342.672.18.244.358.398.504.493l-.454.388a2.775 2.775 0 0 1-.518-.532Zm-1.702 1.24a2.576 2.576 0 0 1-.428-.838l.528-.342c.014.172.1.472.368.831.24.323.48.494.642.57l-.475.392a2.646 2.646 0 0 1-.635-.613Z" fill="#EF5E3E"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="13.178" y1="8.949" x2="13.178" y2="11.199" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="18.036" y1="16.518" x2="18.036" y2="17.921" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#061722"/>
|
||||
<stop offset="1" stop-color="#051622"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="16.617" y1="14.459" x2="16.617" y2="19.605" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="e" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f" x1="13.322" y1="3.238" x2="13.322" y2="7.148" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#DBEBF7"/>
|
||||
<stop offset="1" stop-color="#9AB5C8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="g" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="h" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="i" x1="10.09" y1="3.355" x2="10.09" y2="16.932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="j" x1="13.581" y1="4.344" x2="13.581" y2="6.288" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="k" x1="18.353" y1="1.549" x2="18.353" y2="7.842" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFA271"/>
|
||||
<stop offset="1" stop-color="#F36C24"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
26
app/assets/images/raised-hands.svg
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.204 19.032c3 .6 4.8-.6 4.8-.6v2.867a1.5 1.5 0 0 1-1.5 1.5h-1.8a1.5 1.5 0 0 1-1.5-1.5v-2.267Z" fill="url(#a)"/>
|
||||
<path d="M7.804 19.032c-3 .6-4.8-.6-4.8-.6v2.867a1.5 1.5 0 0 0 1.5 1.5h1.8a1.5 1.5 0 0 0 1.5-1.5v-2.267Z" fill="url(#b)"/>
|
||||
<path d="M8.394 12.743v-.382h-.062c-2.17.03-3.617 1.842-3.624 3.662 0 .367.367.37.367.37.006-2.082 1.319-3.502 3.319-3.65ZM15.608 12.361c2.204-.005 3.679 1.826 3.686 3.663 0 .367-.367.37-.367.37-.006-2.083-1.32-3.502-3.32-3.65v-.383Z" fill="#FFAC2B"/>
|
||||
<path d="M1.428 8.3s.002-.735.736-.733c.736.002.733.737.733.737l-.011 3.41c.117-.053.24-.102.368-.144l.015-4.733s.002-.735.736-.733c.734.002.733.737.733.737l-.013 4.452c.12-.01.242-.019.366-.025l.017-5.161s.002-.735.737-.732c.735.002.732.737.732.737l-.017 5.16c.126.008.247.02.368.032l.015-4.824s.003-.735.736-.732c.735.002.732.736.732.736l-.015 5.101-.001.409v.366c-2.205-.005-3.68 1.825-3.687 3.663 0 .367.367.37.367.37.006-2.208 1.48-3.67 3.686-3.663l1.08-2.513s.237-.695.934-.457c.695.235.459.932.459.932l-.536 1.863c-.212.74-.411 1.485-.515 2.246a4.412 4.412 0 0 1-4.38 3.798 4.41 4.41 0 0 1-4.393-4.422c0-.128.008-.25.02-.366l-.019-.002.017-5.509Zm21.146 0s-.001-.735-.736-.733c-.735.002-.734.737-.734.737l.012 3.41a3.386 3.386 0 0 0-.367-.144l-.015-4.733s-.003-.735-.737-.733c-.735.002-.733.737-.733.737l.013 4.452a8.695 8.695 0 0 0-.366-.025l-.016-5.161s-.002-.735-.737-.732c-.735.002-.732.737-.732.737l.017 5.16c-.126.008-.248.02-.369.032L17.06 6.48s-.003-.735-.737-.732c-.734.002-.731.736-.731.736l.014 5.101.002.409v.366c2.205-.005 3.68 1.825 3.687 3.663 0 .367-.367.37-.367.37-.007-2.208-1.48-3.67-3.685-3.663l-1.08-2.513s-.237-.695-.934-.457c-.695.235-.46.932-.46.932l.536 1.863c.213.74.411 1.485.516 2.246a4.412 4.412 0 0 0 4.38 3.798 4.41 4.41 0 0 0 4.392-4.422c0-.128-.008-.25-.02-.366l.02-.002-.018-5.509Z" fill="url(#c)"/>
|
||||
<path d="M3.004 17.582v.849s1.8 1.2 4.8.6v-.906a4.397 4.397 0 0 1-4.8-.543ZM16.204 18.129a4.397 4.397 0 0 0 4.8-.55v.853s-1.8 1.2-4.8.6v-.903Z" fill="#FFAC2B"/>
|
||||
<path d="M15.329 5.074a.6.6 0 0 1-.474-.967l1.787-2.313a.6.6 0 1 1 .95.734l-1.788 2.313a.597.597 0 0 1-.475.233Zm-6.612 0a.6.6 0 0 1-.475-.233L6.454 2.528a.6.6 0 0 1 .95-.734l1.788 2.313a.6.6 0 0 1-.475.967Zm3.287-.275a.6.6 0 0 1-.6-.6V1.8a.6.6 0 0 1 1.2 0v2.4a.6.6 0 0 1-.6.6Z" fill="url(#d)"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="12.004" y1="19.261" x2="12.004" y2="22.8" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC49"/>
|
||||
<stop offset="1" stop-color="#FFB649"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="12.004" y1="19.261" x2="12.004" y2="22.8" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC49"/>
|
||||
<stop offset="1" stop-color="#FFB649"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="12.001" y1="10.798" x2="12.001" y2="18.599" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC49"/>
|
||||
<stop offset="1" stop-color="#FFB649"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="12.023" y1="1.199" x2="12.023" y2="5.074" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5DADEC"/>
|
||||
<stop offset="1" stop-color="#7870F5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
12
app/assets/images/sparkle-heart.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<svg fill="none" height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg">
|
||||
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="15.908" x2="15.908" y1="3.199" y2="28.854">
|
||||
<stop offset="0" stop-color="#fd556a"/>
|
||||
<stop offset="1" stop-color="#aa172a"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="16.443" x2="16.443" y1="4.828" y2="26.958">
|
||||
<stop offset="0" stop-color="#ffdc5d"/>
|
||||
<stop offset="1" stop-color="#ffcc14"/>
|
||||
</linearGradient>
|
||||
<path d="M30.216 11.094a7.894 7.894 0 0 0-7.894-7.895 7.88 7.88 0 0 0-6.414 3.303A7.88 7.88 0 0 0 9.494 3.2 7.894 7.894 0 0 0 1.6 11.094c0 .617.078 1.216.213 1.792 1.096 6.81 8.668 13.996 14.095 15.968 5.426-1.972 13-9.157 14.094-15.967a7.796 7.796 0 0 0 .214-1.793z" fill="url(#a)"/>
|
||||
<path d="m28.986 20.743-3.06-1.132-1.132-3.06a.8.8 0 0 0-1.501 0L22.16 19.61l-3.06 1.133a.8.8 0 0 0 0 1.5l3.06 1.133 1.133 3.06a.8.8 0 0 0 1.5 0l1.133-3.06 3.06-1.133a.8.8 0 0 0 0-1.5zm-18.4-12.8-1.892-.7-.7-1.892a.8.8 0 0 0-1.501 0l-.701 1.892-1.891.7a.8.8 0 0 0 0 1.5l1.892.7.7 1.892a.8.8 0 0 0 1.5 0l.7-1.892 1.893-.7a.8.8 0 0 0 0-1.5z" fill="url(#b)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
7
app/assets/images/twemoji/raised_hands.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path fill="#FFDC5D" d="M3 26h8v10H3zm22 0h8v10h-8z"/>
|
||||
<path fill="#F9CA55" d="M33 28.72s-3 2-8 1v-5h8v4zm-30 0s3 2 8 1v-5H3v4z"/>
|
||||
<path fill="#EF9645" d="M3.983 18.604h8v8h-8zm20.023-.5h8v8h-8z"/>
|
||||
<path fill="#FFDC5D" d="M.373 11.835S.376 10.61 1.6 10.613c1.226.004 1.222 1.229 1.222 1.229l-.019 5.684a5.66 5.66 0 0 1 .613-.241l.025-7.889s.004-1.225 1.227-1.221c1.224.003 1.221 1.229 1.221 1.229l-.021 7.42c.199-.018.404-.032.61-.042l.028-8.602s.003-1.225 1.228-1.22c1.225.004 1.22 1.229 1.22 1.229l-.028 8.6c.21.012.412.033.614.052l.025-8.039s.004-1.225 1.227-1.22c1.224.003 1.219 1.227 1.219 1.227l-.024 8.501-.003.681v.611c-3.674-.009-6.133 3.042-6.144 6.104 0 .612.612.616.612.616.01-3.678 2.467-6.115 6.142-6.104l1.801-4.188s.395-1.159 1.556-.762c1.158.392.765 1.553.765 1.553l-.893 3.105c-.354 1.234-.685 2.476-.859 3.744A7.353 7.353 0 0 1 7.665 29C3.61 28.983.33 25.685.343 21.63a6.16 6.16 0 0 1 .034-.61l-.032-.004.028-9.181zm35.244 0s-.003-1.225-1.227-1.222c-1.226.004-1.223 1.229-1.223 1.229l.019 5.684a5.644 5.644 0 0 0-.612-.241l-.025-7.889s-.004-1.225-1.227-1.221c-1.225.003-1.222 1.229-1.222 1.229l.022 7.42a14.492 14.492 0 0 0-.61-.042l-.027-8.602s-.004-1.225-1.229-1.22c-1.225.004-1.22 1.229-1.22 1.229l.028 8.6c-.21.012-.412.033-.614.052l-.025-8.039s-.005-1.225-1.228-1.22c-1.224.003-1.219 1.227-1.219 1.227l.024 8.501.003.681v.611c3.674-.009 6.133 3.042 6.145 6.104 0 .612-.612.616-.612.616-.011-3.678-2.468-6.115-6.142-6.104l-1.801-4.188s-.394-1.159-1.556-.762c-1.157.392-.765 1.553-.765 1.553l.893 3.105c.354 1.234.685 2.476.859 3.744A7.353 7.353 0 0 0 28.325 29c4.055-.017 7.336-3.315 7.322-7.37a6.16 6.16 0 0 0-.034-.61l.032-.004-.028-9.181z"/>
|
||||
<path d="M23.541 6.458a1 1 0 0 1-.79-1.611L25.73.992a1 1 0 1 1 1.582 1.223L24.333 6.07a.995.995 0 0 1-.792.388zm-11.02 0a.999.999 0 0 1-.792-.389L8.75 2.215A1.001 1.001 0 0 1 10.334.992l2.979 3.855a1.001 1.001 0 0 1-.792 1.611zM18 6a1 1 0 0 1-1-1V1a1 1 0 0 1 2 0v4a1 1 0 0 1-1 1z" fill="#5DADEC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
8
app/assets/images/twemoji/rocket.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path fill="#A0041E" d="m1 17 8-7 16 1 1 16-7 8s.001-5.999-6-12-12-6-12-6z"/>
|
||||
<path fill="#FFAC33" d="M.973 35s-.036-7.979 2.985-11S15 21.187 15 21.187 14.999 29 11.999 32c-3 3-11.026 3-11.026 3z"/>
|
||||
<circle fill="#FFCC4D" cx="8.999" cy="27" r="4"/>
|
||||
<path fill="#55ACEE" d="M35.999 0s-10 0-22 10c-6 5-6 14-4 16s11 2 16-4c10-12 10-22 10-22z"/>
|
||||
<path d="M26.999 5a3.996 3.996 0 0 0-3.641 2.36A3.969 3.969 0 0 1 24.999 7a4 4 0 0 1 4 4c0 .586-.133 1.139-.359 1.64A3.993 3.993 0 0 0 30.999 9a4 4 0 0 0-4-4z"/>
|
||||
<path fill="#A0041E" d="M8 28s0-4 1-5 13.001-10.999 14-10-9.001 13-10.001 14S8 28 8 28z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 687 B |
4
app/assets/images/twemoji/sparkle-heart.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path fill="#DD2E44" d="M35.885 11.833c0-5.45-4.418-9.868-9.867-9.868-3.308 0-6.227 1.633-8.018 4.129-1.791-2.496-4.71-4.129-8.017-4.129-5.45 0-9.868 4.417-9.868 9.868 0 .772.098 1.52.266 2.241C1.751 22.587 11.216 31.568 18 34.034c6.783-2.466 16.249-11.447 17.617-19.959.17-.721.268-1.469.268-2.242z"/>
|
||||
<path fill="#FDCB58" d="m34.347 23.894-3.824-1.416-1.416-3.824a1 1 0 0 0-1.876-.001l-1.416 3.824-3.824 1.416a1.001 1.001 0 0 0 0 1.876l3.824 1.416 1.416 3.824a1 1 0 0 0 1.876 0l1.416-3.824 3.824-1.416a1.001 1.001 0 0 0 0-1.875zm-23-16.001-2.365-.875-.875-2.365a1 1 0 0 0-1.876 0l-.876 2.365-2.364.875a1 1 0 0 0 0 1.876l2.365.875.875 2.365a1 1 0 0 0 1.876 0l.875-2.365 2.365-.875a1 1 0 0 0 0-1.876z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 780 B |
17
app/assets/images/twemoji/tada.svg
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
|
||||
<path fill="#DD2E44" d="M11.626 7.488a1.413 1.413 0 0 0-.268.395l-.008-.008L.134 33.141l.011.011c-.208.403.14 1.223.853 1.937.713.713 1.533 1.061 1.936.853l.01.01L28.21 24.735l-.008-.009c.147-.07.282-.155.395-.269 1.562-1.562-.971-6.627-5.656-11.313-4.687-4.686-9.752-7.218-11.315-5.656z"/>
|
||||
<path fill="#EA596E" d="M13 12 .416 32.506l-.282.635.011.011c-.208.403.14 1.223.853 1.937.232.232.473.408.709.557L17 17l-4-5z"/>
|
||||
<path fill="#A0041E" d="M23.012 13.066c4.67 4.672 7.263 9.652 5.789 11.124-1.473 1.474-6.453-1.118-11.126-5.788-4.671-4.672-7.263-9.654-5.79-11.127 1.474-1.473 6.454 1.119 11.127 5.791z"/>
|
||||
<path fill="#AA8DD8" d="M18.59 13.609a.99.99 0 0 1-.734.215c-.868-.094-1.598-.396-2.109-.873-.541-.505-.808-1.183-.735-1.862.128-1.192 1.324-2.286 3.363-2.066.793.085 1.147-.17 1.159-.292.014-.121-.277-.446-1.07-.532-.868-.094-1.598-.396-2.11-.873-.541-.505-.809-1.183-.735-1.862.13-1.192 1.325-2.286 3.362-2.065.578.062.883-.057 1.012-.134.103-.063.144-.123.148-.158.012-.121-.275-.446-1.07-.532a.998.998 0 0 1-.886-1.102.997.997 0 0 1 1.101-.886c2.037.219 2.973 1.542 2.844 2.735-.13 1.194-1.325 2.286-3.364 2.067-.578-.063-.88.057-1.01.134-.103.062-.145.123-.149.157-.013.122.276.446 1.071.532 2.037.22 2.973 1.542 2.844 2.735-.129 1.192-1.324 2.286-3.362 2.065-.578-.062-.882.058-1.012.134-.104.064-.144.124-.148.158-.013.121.276.446 1.07.532a1 1 0 0 1 .52 1.773z"/>
|
||||
<path fill="#77B255" d="M30.661 22.857c1.973-.557 3.334.323 3.658 1.478.324 1.154-.378 2.615-2.35 3.17-.77.216-1.001.584-.97.701.034.118.425.312 1.193.095 1.972-.555 3.333.325 3.657 1.479.326 1.155-.378 2.614-2.351 3.17-.769.216-1.001.585-.967.702.033.117.423.311 1.192.095a1 1 0 1 1 .54 1.925c-1.971.555-3.333-.323-3.659-1.479-.324-1.154.379-2.613 2.353-3.169.77-.217 1.001-.584.967-.702-.032-.117-.422-.312-1.19-.096-1.974.556-3.334-.322-3.659-1.479-.325-1.154.378-2.613 2.351-3.17.768-.215.999-.585.967-.701-.034-.118-.423-.312-1.192-.096a1 1 0 1 1-.54-1.923z"/>
|
||||
<path fill="#AA8DD8" d="M23.001 20.16a1.001 1.001 0 0 1-.626-1.781c.218-.175 5.418-4.259 12.767-3.208a1 1 0 1 1-.283 1.979c-6.493-.922-11.187 2.754-11.233 2.791a.999.999 0 0 1-.625.219z"/>
|
||||
<path fill="#77B255" d="M5.754 16a1 1 0 0 1-.958-1.287c1.133-3.773 2.16-9.794.898-11.364-.141-.178-.354-.353-.842-.316-.938.072-.849 2.051-.848 2.071a1 1 0 1 1-1.994.149c-.103-1.379.326-4.035 2.692-4.214 1.056-.08 1.933.287 2.552 1.057 2.371 2.951-.036 11.506-.542 13.192a1 1 0 0 1-.958.712z"/>
|
||||
<circle fill="#5C913B" cx="25.5" cy="9.5" r="1.5"/>
|
||||
<circle fill="#9266CC" cx="2" cy="18" r="2"/>
|
||||
<circle fill="#5C913B" cx="32.5" cy="19.5" r="1.5"/>
|
||||
<circle fill="#5C913B" cx="23.5" cy="31.5" r="1.5"/>
|
||||
<circle fill="#FFCC4D" cx="28" cy="4" r="2"/>
|
||||
<circle fill="#FFCC4D" cx="32.5" cy="8.5" r="1.5"/>
|
||||
<circle fill="#FFCC4D" cx="29.5" cy="12.5" r="1.5"/>
|
||||
<circle fill="#FFCC4D" cx="7.5" cy="23.5" r="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -1,19 +1,29 @@
|
|||
/* global sendHapticMessage, showLoginModal, showModalAfterError */
|
||||
/* global sendHapticMessage, showLoginModal, showModalAfterError, isTouchDevice, watchForLongTouch */
|
||||
|
||||
// Set reaction count to correct number
|
||||
function setReactionCount(reactionName, newCount) {
|
||||
var reactionClassList = document.getElementById(
|
||||
var reactionButtons = document.getElementById(
|
||||
'reaction-butt-' + reactionName,
|
||||
).classList;
|
||||
var reactionNumber = document.getElementById(
|
||||
var reactionButtonCounter = document.getElementById(
|
||||
'reaction-number-' + reactionName,
|
||||
);
|
||||
var reactionEngagementCounter = document.getElementById(
|
||||
'reaction_engagement_' + reactionName + '_count',
|
||||
);
|
||||
if (newCount > 0) {
|
||||
reactionClassList.add('activated');
|
||||
reactionNumber.textContent = newCount;
|
||||
reactionButtons.add('activated');
|
||||
reactionButtonCounter.textContent = newCount;
|
||||
if (reactionEngagementCounter) {
|
||||
reactionEngagementCounter.parentElement.classList.remove('hidden');
|
||||
reactionEngagementCounter.textContent = newCount;
|
||||
}
|
||||
} else {
|
||||
reactionClassList.remove('activated');
|
||||
reactionNumber.textContent = '0';
|
||||
reactionButtons.remove('activated');
|
||||
reactionButtonCounter.textContent = '0';
|
||||
if (reactionEngagementCounter) {
|
||||
reactionEngagementCounter.parentElement.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +33,41 @@ function showUserReaction(reactionName, animatedClass) {
|
|||
);
|
||||
reactionButton.classList.add('user-activated', animatedClass);
|
||||
reactionButton.setAttribute('aria-pressed', 'true');
|
||||
|
||||
const reactionDrawerButton = document.getElementById(
|
||||
'reaction-drawer-trigger',
|
||||
);
|
||||
|
||||
// the rest only applies to multiple reactions feature flag
|
||||
if (!reactionDrawerButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reactionDrawerButton && reactionName !== 'readinglist') {
|
||||
reactionDrawerButton.classList.add('user-activated', 'user-animated');
|
||||
}
|
||||
|
||||
if (animatedClass == 'user-animated') {
|
||||
const activeIcon = reactionButton.querySelector(
|
||||
'.crayons-reaction__icon--active svg',
|
||||
);
|
||||
|
||||
if (activeIcon) {
|
||||
const activeDrawerIcon = reactionDrawerButton.querySelector(
|
||||
'.crayons-reaction__icon--active img',
|
||||
);
|
||||
|
||||
reactionDrawerButton.originalIcon = activeDrawerIcon.outerHTML;
|
||||
activeDrawerIcon.outerHTML = activeIcon.outerHTML;
|
||||
|
||||
setTimeout(function () {
|
||||
document
|
||||
.getElementById('reaction-drawer-trigger')
|
||||
.querySelector('.crayons-reaction__icon--active svg').outerHTML =
|
||||
reactionDrawerButton.originalIcon;
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hideUserReaction(reactionName) {
|
||||
|
|
@ -49,15 +94,25 @@ function getNumReactions(reactionName) {
|
|||
}
|
||||
|
||||
function reactToArticle(articleId, reaction) {
|
||||
var reactionTotalCount = document.getElementById('reaction_total_count');
|
||||
|
||||
var isReadingList = reaction === 'readinglist';
|
||||
|
||||
// Visually toggle the reaction
|
||||
function toggleReaction() {
|
||||
var currentNum = getNumReactions(reaction);
|
||||
if (hasUserReacted(reaction)) {
|
||||
hideUserReaction(reaction);
|
||||
setReactionCount(reaction, currentNum - 1);
|
||||
if (reactionTotalCount && !isReadingList) {
|
||||
reactionTotalCount.innerText = Number(reactionTotalCount.innerText) - 1;
|
||||
}
|
||||
} else {
|
||||
showUserReaction(reaction, 'user-animated');
|
||||
setReactionCount(reaction, currentNum + 1);
|
||||
if (reactionTotalCount && !isReadingList) {
|
||||
reactionTotalCount.innerText = Number(reactionTotalCount.innerText) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
var userStatus = document.body.getAttribute('data-user-status');
|
||||
|
|
@ -153,9 +208,47 @@ function requestReactionCounts(articleId) {
|
|||
ajaxReq.send();
|
||||
}
|
||||
|
||||
function openDrawerOnHover() {
|
||||
var timer;
|
||||
const drawerTrigger = document.getElementById('reaction-drawer-trigger');
|
||||
if (!drawerTrigger) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawerTrigger.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
if (isTouchDevice()) {
|
||||
watchForLongTouch(drawerTrigger);
|
||||
drawerTrigger.addEventListener('longTouch', function () {
|
||||
drawerTrigger.parentElement.classList.add('open');
|
||||
});
|
||||
document.addEventListener('touchstart', function (event) {
|
||||
if (!drawerTrigger.parentElement.contains(event.target)) {
|
||||
drawerTrigger.parentElement.classList.remove('open');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.querySelectorAll('.hoverdown').forEach(function (el) {
|
||||
el.addEventListener('mouseover', function (event) {
|
||||
this.classList.add('open');
|
||||
clearTimeout(timer);
|
||||
});
|
||||
el.addEventListener('mouseout', function (event) {
|
||||
timer = setTimeout(function (event) {
|
||||
document.querySelector('.hoverdown.open').classList.remove('open');
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initializeArticleReactions() {
|
||||
setCollectionFunctionality();
|
||||
|
||||
openDrawerOnHover();
|
||||
|
||||
setTimeout(() => {
|
||||
var reactionButts = document.getElementsByClassName('crayons-reaction');
|
||||
|
||||
|
|
@ -167,6 +260,9 @@ function initializeArticleReactions() {
|
|||
requestReactionCounts(articleId);
|
||||
|
||||
for (var i = 0; i < reactionButts.length; i += 1) {
|
||||
if (reactionButts[i].classList.contains('pseudo-reaction')) {
|
||||
continue;
|
||||
}
|
||||
reactionButts[i].onclick = function addReactionOnClick(e) {
|
||||
reactToArticle(articleId, this.dataset.category);
|
||||
};
|
||||
|
|
@ -174,13 +270,15 @@ function initializeArticleReactions() {
|
|||
}
|
||||
|
||||
var jumpToCommentsButt = document.getElementById('reaction-butt-comment');
|
||||
var commentsSection = document.getElementById("comments");
|
||||
if (document.getElementById('article-body') && commentsSection && jumpToCommentsButt) {
|
||||
|
||||
var commentsSection = document.getElementById('comments');
|
||||
if (
|
||||
document.getElementById('article-body') &&
|
||||
commentsSection &&
|
||||
jumpToCommentsButt
|
||||
) {
|
||||
jumpToCommentsButt.onclick = function jumpToComments(e) {
|
||||
commentsSection.scrollIntoView({behavior: "smooth"});
|
||||
commentsSection.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
}
|
||||
}, 3);
|
||||
}
|
||||
|
|
|
|||
10
app/assets/javascripts/utilities/isTouchDevice.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Determines whether or not a device is a touch device.
|
||||
*
|
||||
* @returns true if a touch device, otherwise false.
|
||||
*/
|
||||
function isTouchDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|DEV-Native-ios/i.test(
|
||||
navigator.userAgent,
|
||||
);
|
||||
}
|
||||
40
app/assets/javascripts/utilities/watchForLongTouch.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Watch @param element for a "long touch" (800ms) and dispatch a custom "longTouch" event,
|
||||
// which you are presumably listening for elsewhere. Note that this also disables context menu
|
||||
// for the element, as otherwise device will default to opening a context menu instead.
|
||||
function watchForLongTouch(element) {
|
||||
const longTouchEvent = new Event('longTouch');
|
||||
const minDuration = 800;
|
||||
let timer;
|
||||
|
||||
// In the event of a long touch, dispatch the "longTouch" event
|
||||
const dispatcher = function () {
|
||||
element.dispatchEvent(longTouchEvent);
|
||||
};
|
||||
|
||||
// Observe "touchstart", if it's still going after a duration, dispatch "longTouch"
|
||||
const onTouchStart = function (event) {
|
||||
if (!timer) {
|
||||
timer = setTimeout(dispatcher, minDuration);
|
||||
}
|
||||
};
|
||||
|
||||
// Observe "touchend", if they've stopped touching, it won't become a longTouch anytime soon
|
||||
const onTouchEnd = function () {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (element) {
|
||||
// Disable contextmenu on device, otherwise they'll get a device OS menu instead of ours
|
||||
element.oncontextmenu = function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation(); // not necessary in my case, could leave in case stopImmediateProp isn't available?
|
||||
event.stopImmediatePropagation();
|
||||
return false;
|
||||
};
|
||||
element.addEventListener('touchstart', onTouchStart);
|
||||
element.addEventListener('touchend', onTouchEnd);
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
--radius: 0.375rem;
|
||||
--radius-large: 0.75rem;
|
||||
--radius-xl: 2rem;
|
||||
// 1. this is a trick for conditional border-radius: https://ishadeed.com/article/conditional-border-radius/
|
||||
// 2. intentionally using uppercase Max and Min because SCSS have issues otherwise...
|
||||
--radius-auto: Max(0px, Min(var(--radius), calc((100vw - 4px - 100%) * 9999))) /
|
||||
|
|
|
|||
|
|
@ -241,6 +241,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
// This overrides some of the styles for the temporarily feature-flaged
|
||||
.reaction-drawer .crayons-reaction {
|
||||
border: 1px solid var(--card-bg);
|
||||
flex-direction: column;
|
||||
|
||||
&.user-activated {
|
||||
background-color: var(--body-bg);
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 20px;
|
||||
|
||||
.crayons-reaction__icon--active {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-animated {
|
||||
.crayons-reaction__icon {
|
||||
position: relative;
|
||||
|
|
@ -349,3 +366,107 @@
|
|||
color: var(--card-color-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The drawer__outer contains the trigger button and the drawer itself,
|
||||
// enabling hover, however this then requires the __outer wrapping div to be
|
||||
// styled similarly to the other action buttons in the sidebar, which we can
|
||||
// clean up significantly *after* the multiple_reactions feature flag is
|
||||
// fully rolled-out.
|
||||
.reaction-drawer__outer {
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
@media (min-width: $breakpoint-m) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.open .reaction-drawer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.reaction-drawer {
|
||||
min-width: auto;
|
||||
border-radius: 32px;
|
||||
position: absolute;
|
||||
top: -80px;
|
||||
left: 15px;
|
||||
display: none; // Initially hidden
|
||||
|
||||
// Copying from Dropdown for now
|
||||
padding: var(--su-2);
|
||||
min-width: 250px;
|
||||
margin-top: var(--su-1);
|
||||
z-index: var(--z-dropdown);
|
||||
background: var(--card-bg);
|
||||
color: var(--card-color);
|
||||
box-shadow: var(--shadow-1);
|
||||
border-radius: var(--radius);
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
@media (min-width: $breakpoint-s) {
|
||||
max-width: 360px;
|
||||
width: max-content;
|
||||
top: -5px;
|
||||
left: 55px;
|
||||
}
|
||||
|
||||
.crayons-reaction__count {
|
||||
min-width: auto;
|
||||
margin-left: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.reaction-drawer__outer:hover .reaction-drawer {
|
||||
@media (min-width: $breakpoint-m) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.reaction-drawer__container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--su-3);
|
||||
}
|
||||
|
||||
.multiple_reactions_engagement {
|
||||
padding: 0.5em 0;
|
||||
|
||||
span {
|
||||
margin-right: 1em;
|
||||
}
|
||||
svg {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.multiple_reactions_aggregate {
|
||||
padding-right: 1em;
|
||||
|
||||
.crayons_icon_container {
|
||||
display: inline-block;
|
||||
background-color: var(--body-bg);
|
||||
border-radius: 15px;
|
||||
margin-right: -0.75em;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin: 3px 3px;
|
||||
}
|
||||
|
||||
.aggregate_reactions_counter {
|
||||
margin-left: 0.75em;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,16 +12,8 @@ class ReactionsController < ApplicationController
|
|||
if params[:article_id]
|
||||
id = params[:article_id]
|
||||
|
||||
reactions = if session_current_user_id
|
||||
Reaction.public_category
|
||||
.where(
|
||||
reactable_id: id,
|
||||
reactable_type: "Article",
|
||||
user_id: session_current_user_id,
|
||||
)
|
||||
else
|
||||
Reaction.none
|
||||
end
|
||||
reactions = reactions_by_user_id(session_current_user_id) if session_current_user_id
|
||||
reactions ||= Reaction.none
|
||||
|
||||
result = { article_reaction_counts: Reaction.count_for_article(id) }
|
||||
else
|
||||
|
|
@ -87,6 +79,23 @@ class ReactionsController < ApplicationController
|
|||
authorize(Reaction, policy_query)
|
||||
end
|
||||
|
||||
def reactions_by_user_id(user_id = session_current_user_id)
|
||||
id = params[:article_id]
|
||||
|
||||
public_reactions = Reaction.public_category.where(
|
||||
reactable_id: id,
|
||||
reactable_type: "Article",
|
||||
user_id: user_id,
|
||||
)
|
||||
readinglist = Reaction.where(
|
||||
reactable_id: id,
|
||||
reactable_type: "Article",
|
||||
category: "readinglist", user_id: user_id
|
||||
)
|
||||
|
||||
public_reactions + readinglist
|
||||
end
|
||||
|
||||
# TODO: should this move to toggle service? refactor?
|
||||
def remove_count_cache_key
|
||||
return unless params[:reactable_type] == "Article"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ module CrayonsHelper
|
|||
opts.delete(:class),
|
||||
].compact.join(" ")
|
||||
|
||||
inline_svg_tag(icon_name, aria: true, width: 24, height: 24, class: icon_class, **opts)
|
||||
opts[:width] ||= 24
|
||||
opts[:height] ||= 24
|
||||
|
||||
inline_svg_tag(icon_name, aria: true, class: icon_class, **opts)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,6 +34,20 @@ if (snackZone) {
|
|||
// eslint-disable-next-line no-restricted-globals
|
||||
top.addSnackbarItem = addSnackbarItem;
|
||||
|
||||
const multiReactionDrawerTrigger = document.getElementById(
|
||||
'reaction-drawer-trigger',
|
||||
);
|
||||
|
||||
if (
|
||||
multiReactionDrawerTrigger &&
|
||||
multiReactionDrawerTrigger.dataset.initialized !== 'true'
|
||||
) {
|
||||
initializeDropdown({
|
||||
triggerElementId: 'reaction-drawer-trigger',
|
||||
dropdownContentId: 'reaction-drawer',
|
||||
});
|
||||
}
|
||||
|
||||
// Dropdown accessibility
|
||||
function hideCopyLinkAnnouncerIfVisible() {
|
||||
document.getElementById('article-copy-link-announcer').hidden = true;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* global isTouchDevice */
|
||||
|
||||
function closeHeaderMenu(memberMenu, menuNavButton) {
|
||||
menuNavButton.setAttribute('aria-expanded', 'false');
|
||||
memberMenu.classList.remove('desktop', 'showing');
|
||||
|
|
@ -28,17 +30,6 @@ function openHeaderMenu(memberMenu, menuNavButton) {
|
|||
})();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not a device is a touch device.
|
||||
*
|
||||
* @returns true if a touch device, otherwise false.
|
||||
*/
|
||||
export function isTouchDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|DEV-Native-ios/i.test(
|
||||
navigator.userAgent,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the member navigation menu events.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,4 +8,8 @@ module Reactable
|
|||
def sync_reactions_count
|
||||
update_column(:public_reactions_count, reactions.public_category.size)
|
||||
end
|
||||
|
||||
def reaction_categories
|
||||
reactions.distinct(:category).pluck(:category)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,11 +7,16 @@ class Reaction < ApplicationRecord
|
|||
|
||||
counter_culture :reactable,
|
||||
column_name: proc { |model|
|
||||
ReactionCategory[model.category].visible_to_public? ? "public_reactions_count" : "reactions_count"
|
||||
# After FeatureFlag :multiple_reactions, this could change to:
|
||||
# ReactionCategory[model.category].visible_to_public?
|
||||
public_reaction_types.include?(model.category.to_s) ? "public_reactions_count" : "reactions_count"
|
||||
}
|
||||
counter_culture :user
|
||||
|
||||
scope :public_category, -> { where(category: ReactionCategory.public.map(&:to_s)) }
|
||||
scope :public_category, lambda {
|
||||
categories = public_reaction_types
|
||||
where(category: categories)
|
||||
}
|
||||
|
||||
# Be wary, this is all things on the reading list, but for an end
|
||||
# user they might only see readinglist items that are published.
|
||||
|
|
@ -55,10 +60,8 @@ class Reaction < ApplicationRecord
|
|||
reactions = Reaction.where(reactable_id: id, reactable_type: "Article")
|
||||
counts = reactions.group(:category).count
|
||||
|
||||
reaction_types = %w[like readinglist]
|
||||
unless FeatureFlag.enabled?(:replace_unicorn_with_jump_to_comments)
|
||||
reaction_types << "unicorn"
|
||||
end
|
||||
reaction_types = public_reaction_types
|
||||
reaction_types << "readinglist" unless public_reaction_types.include?("readinglist")
|
||||
|
||||
reaction_types.map do |type|
|
||||
{ category: type, count: counts.fetch(type, 0) }
|
||||
|
|
@ -75,6 +78,27 @@ class Reaction < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def public_reaction_types
|
||||
if FeatureFlag.enabled?(:multiple_reactions)
|
||||
reaction_types = ReactionCategory.public.map(&:to_s) - ["readinglist"]
|
||||
else
|
||||
# used to include "readinglist" but that's not correct now, even without the feature flag
|
||||
# we aren't going to re-process these, they will gradually correct over time
|
||||
reaction_types = %w[like]
|
||||
unless FeatureFlag.enabled?(:replace_unicorn_with_jump_to_comments)
|
||||
reaction_types << "unicorn"
|
||||
end
|
||||
end
|
||||
|
||||
reaction_types
|
||||
end
|
||||
|
||||
def for_analytics
|
||||
reaction_types = public_reaction_types
|
||||
reaction_types << "readinglist" unless public_reaction_types.include?("readinglist")
|
||||
where(category: reaction_types)
|
||||
end
|
||||
|
||||
# @param user [User] the user who might be spamming the system
|
||||
# @param threshold [Integer] the number of strikes before they are spam
|
||||
# @param include_user_profile [Boolean] do we include the user's profile as part of the "check
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ class ReactionCategory
|
|||
list.filter_map { |category| category.slug if category.privileged? && category.negative? }
|
||||
end
|
||||
|
||||
def for_view
|
||||
list.sort_by(&:position).filter_map { |category| category if category.visible_to_public? }
|
||||
end
|
||||
|
||||
def public
|
||||
list.sort_by(&:position).filter_map { |category| category.slug if category.visible_to_public? }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class AnalyticsService
|
|||
.where("score > 0")
|
||||
@follow_data = Follow
|
||||
.where(followable_type: user_or_org.class.name, followable_id: user_or_org.id)
|
||||
@reaction_data = Reaction.public_category
|
||||
@reaction_data = Reaction.for_analytics
|
||||
.where(reactable_id: article_ids, reactable_type: "Article")
|
||||
@page_view_data = PageView.where(article_id: article_ids)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +1,11 @@
|
|||
<div class="crayons-article-actions print-hidden">
|
||||
<div class="crayons-article-actions__inner">
|
||||
<% if @article.published? %>
|
||||
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :like,
|
||||
description: t("views.reactions.like.title"),
|
||||
image_path: "heart.svg",
|
||||
image_active_path: "heart-filled.svg",
|
||||
aria_label: t("views.reactions.like.aria_label")
|
||||
} %>
|
||||
|
||||
<% if FeatureFlag.enabled?(:replace_unicorn_with_jump_to_comments) %>
|
||||
<button
|
||||
id="reaction-butt-comment"
|
||||
aria-label="<%= t("views.articles.jump_to_comments.aria_label") %>"
|
||||
aria-pressed="false"
|
||||
class="crayons-reaction crayons-reaction--comment crayons-tooltip__activator relative"
|
||||
data-category="comment">
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--inactive">
|
||||
<%= crayons_icon_tag("comment.svg", aria_hidden: true) %>
|
||||
</span>
|
||||
<span class="crayons-reaction__count" id="reaction-number-comment"><%= @article.comments_count %></span>
|
||||
|
||||
<span data-testid="tooltip" class="crayons-tooltip__content">
|
||||
<%= t("views.articles.jump_to_comments.label") %>
|
||||
</span>
|
||||
</button>
|
||||
<% if FeatureFlag.enabled?(:multiple_reactions) %>
|
||||
<%= render partial: "articles/multiple_reactions" %>
|
||||
<% else %>
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :unicorn,
|
||||
description: t("views.reactions.unicorn.title"),
|
||||
image_path: "unicorn.svg",
|
||||
image_active_path: "unicorn-filled.svg",
|
||||
aria_label: t("views.reactions.unicorn.aria_label")
|
||||
} %>
|
||||
<%= render partial: "articles/original_reactions" %>
|
||||
<% end %>
|
||||
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :readinglist,
|
||||
description: t("views.reactions.readingList.title"),
|
||||
image_path: "save.svg",
|
||||
image_active_path: "save-filled.svg",
|
||||
aria_label: t("views.reactions.readingList.aria_label")
|
||||
} %>
|
||||
<% end %>
|
||||
<div id="mod-actions-menu-btn-area" class="print-hidden hidden align-center"></div>
|
||||
<div class="align-center m:relative">
|
||||
|
|
|
|||
10
app/views/articles/_multiple_engagements.html.erb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<div class="multiple_reactions_engagement">
|
||||
<% ReactionCategory.for_view.each do |reaction_type| %>
|
||||
<span class="reaction_engagement_<%= reaction_type.slug %> hidden">
|
||||
<%= crayons_icon_tag(reaction_type.icon, native: true, aria_hidden: true, force_unique: true) %>
|
||||
<span id="reaction_engagement_<%= reaction_type.slug %>_count"> </span>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<span class="placeholder" style="width: 24px; height: 24px;"> </span>
|
||||
</div>
|
||||
61
app/views/articles/_multiple_reactions.html.erb
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
<div class="reaction-drawer__outer hoverdown" style="">
|
||||
<button
|
||||
id="reaction-drawer-trigger"
|
||||
aria-label="reaction-drawer-trigger"
|
||||
aria-pressed="false"
|
||||
class="hoverdown-trigger crayons-reaction pseudo-reaction crayons-tooltip__activator relative">
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--inactive" style="width: 40px; height: 40px">
|
||||
<%= image_tag "heart-plus.svg", aria_hidden: true, height: 24, width: 24 %>
|
||||
</span>
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--active" style="width: 40px; height: 40px">
|
||||
<%= image_tag "heart-plus-active.svg", aria_hidden: true, height: 24, width: 24 %>
|
||||
</span>
|
||||
<span class="crayons-reaction_total_count" id="reaction_total_count">
|
||||
<%= @article.public_reactions_count %>
|
||||
</span>
|
||||
<span data-testid="tooltip" class="crayons-tooltip__content">
|
||||
<%= t("views.reactions.drawer_button") %>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="reaction-drawer" aria-expanded="false">
|
||||
<div class="reaction-drawer__container">
|
||||
<% ReactionCategory.for_view.each do |reaction_type| %>
|
||||
<%= render partial: "articles/multireaction_button",
|
||||
locals: {
|
||||
category: reaction_type.slug.to_sym,
|
||||
description: reaction_type.name,
|
||||
image_path: "#{reaction_type.icon}.svg",
|
||||
image_active_path: "#{reaction_type.icon}.svg",
|
||||
aria_label: reaction_type.name
|
||||
} %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="reaction-butt-comment"
|
||||
aria-label="<%= t("views.articles.jump_to_comments.aria_label") %>"
|
||||
aria-pressed="false"
|
||||
class="crayons-reaction crayons-reaction--comment crayons-tooltip__activator relative"
|
||||
data-category="comment">
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--inactive">
|
||||
<%= crayons_icon_tag("comment.svg", aria_hidden: true) %>
|
||||
</span>
|
||||
<span class="crayons-reaction__count" id="reaction-number-comment"><%= @article.comments_count %></span>
|
||||
|
||||
<span data-testid="tooltip" class="crayons-tooltip__content">
|
||||
<%= t("views.articles.jump_to_comments.label") %>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :readinglist,
|
||||
description: t("views.reactions.readingList.title"),
|
||||
image_path: "save.svg",
|
||||
image_active_path: "save-filled.svg",
|
||||
aria_label: t("views.reactions.readingList.aria_label")
|
||||
} %>
|
||||
23
app/views/articles/_multireaction_button.html.erb
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<button
|
||||
id="reaction-butt-<%= category %>"
|
||||
aria-label="<%= aria_label %>"
|
||||
aria-pressed="false"
|
||||
class="crayons-reaction crayons-tooltip__activator relative"
|
||||
data-category="<%= category %>">
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--inactive">
|
||||
<%= crayons_icon_tag(image_path, native: true, aria_hidden: true, force_unique: true) %>
|
||||
<%#= image_tag image_path, aria_hidden: true, height: 24, width: 24 %>
|
||||
</span>
|
||||
<%# TODO: This can be straightened out after feature-flag -- double icons are not required now %>
|
||||
<% if user_signed_in? # We cannot trigger the action state unless the user is signed in, so no need to render. %>
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--active">
|
||||
<%= crayons_icon_tag(image_active_path, native: true, aria_hidden: true, force_unique: true) %>
|
||||
<%#= image_tag image_active_path, aria_hidden: true, height: 24, width: 24 %>
|
||||
</span>
|
||||
<% end %>
|
||||
<span class="crayons-reaction__count" id="reaction-number-<%= category %>"><span class="bg-base-40 opacity-25 p-2 inline-block radius-default"></span></span>
|
||||
|
||||
<span data-testid="tooltip" class="crayons-tooltip__content">
|
||||
<%= description %>
|
||||
</span>
|
||||
</button>
|
||||
44
app/views/articles/_original_reactions.html.erb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :like,
|
||||
description: t("views.reactions.like.title"),
|
||||
image_path: "heart.svg",
|
||||
image_active_path: "heart-filled.svg",
|
||||
aria_label: t("views.reactions.like.aria_label")
|
||||
} %>
|
||||
|
||||
<% if FeatureFlag.enabled?(:replace_unicorn_with_jump_to_comments) %>
|
||||
<button
|
||||
id="reaction-butt-comment"
|
||||
aria-label="<%= t("views.articles.jump_to_comments.aria_label") %>"
|
||||
aria-pressed="false"
|
||||
class="crayons-reaction crayons-reaction--comment crayons-tooltip__activator relative"
|
||||
data-category="comment">
|
||||
<span class="crayons-reaction__icon crayons-reaction__icon--inactive">
|
||||
<%= crayons_icon_tag("comment.svg", aria_hidden: true) %>
|
||||
</span>
|
||||
<span class="crayons-reaction__count" id="reaction-number-comment"><%= @article.comments_count %></span>
|
||||
|
||||
<span data-testid="tooltip" class="crayons-tooltip__content">
|
||||
<%= t("views.articles.jump_to_comments.label") %>
|
||||
</span>
|
||||
</button>
|
||||
<% else %>
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :unicorn,
|
||||
description: t("views.reactions.unicorn.title"),
|
||||
image_path: "unicorn.svg",
|
||||
image_active_path: "unicorn-filled.svg",
|
||||
aria_label: t("views.reactions.unicorn.aria_label")
|
||||
} %>
|
||||
<% end %>
|
||||
|
||||
<%= render partial: "articles/reaction_button",
|
||||
locals: {
|
||||
category: :readinglist,
|
||||
description: t("views.reactions.readingList.title"),
|
||||
image_path: "save.svg",
|
||||
image_active_path: "save-filled.svg",
|
||||
aria_label: t("views.reactions.readingList.aria_label")
|
||||
} %>
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
<%= render partial: "shared/auth_widget", locals: { tracking_id: "ca_left_sidebar_home_page", source: "left_sidebar" } unless user_signed_in? %>
|
||||
<%= render partial: "layouts/main_nav", locals: { context: "sidebar" } %>
|
||||
<%= render "layouts/sidebar_tags" %>
|
||||
<%# the pattern .present?/.each has the advantage of issuing only a single SQL query to load objects in memory %>
|
||||
<% end %>
|
||||
<% cache("display-area-left-#{rand(5)}-#{user_signed_in?}", expires_in: 5.minutes) do %>
|
||||
<% @left_sidebar_ad = DisplayAd.for_display("sidebar_left", user_signed_in?) %>
|
||||
|
|
|
|||
|
|
@ -120,13 +120,28 @@
|
|||
<div class="crayons-story__bottom">
|
||||
<div class="crayons-story__details">
|
||||
<% if story.public_reactions_count > 0 %>
|
||||
<a href="<%= story.path %>" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left" data-reaction-count data-reactable-id="<%= story.id %>" aria-label="<%= t("views.articles.comments.aria_label", title: story.title) %>">
|
||||
<%= crayons_icon_tag("small-heart", title: t("views.reactions.summary.title")) %>
|
||||
<%= t("views.reactions.summary.count_html",
|
||||
count: story.public_reactions_count,
|
||||
start: tag("span", { class: %w[hidden s:inline] }, true),
|
||||
end: "</span>".html_safe) %>
|
||||
</a>
|
||||
<% if FeatureFlag.enabled?(:multiple_reactions) %>
|
||||
<div class="multiple_reactions_aggregate">
|
||||
<% ReactionCategory.for_view.each do |reaction_type| %>
|
||||
<% next unless story.reaction_categories.include?(reaction_type.slug.to_s) %>
|
||||
<span class="crayons_icon_container">
|
||||
<%= crayons_icon_tag(reaction_type.icon, width: "18px", height: "18px", native: true, aria_hidden: true) %>
|
||||
</span>
|
||||
<% end %>
|
||||
<span class="aggregate_reactions_counter"><%= t("views.reactions.summary.count_html",
|
||||
count: story.public_reactions_count,
|
||||
start: tag("span", { class: %w[hidden s:inline] }, true),
|
||||
end: "</span>".html_safe) %></span>
|
||||
</div>
|
||||
<% else %>
|
||||
<a href="<%= story.path %>" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left" data-reaction-count data-reactable-id="<%= story.id %>" aria-label="<%= t("views.articles.comments.aria_label", title: story.title) %>">
|
||||
<%= crayons_icon_tag("small-heart", title: t("views.reactions.summary.title")) %>
|
||||
<%= t("views.reactions.summary.count_html",
|
||||
count: story.public_reactions_count,
|
||||
start: tag("span", { class: %w[hidden s:inline] }, true),
|
||||
end: "</span>".html_safe) %>
|
||||
</a>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if story.comments_count > 0 %>
|
||||
<a href="<%= story.path %>#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left" aria-label="<%= t("views.articles.comments.aria_label", title: story.title) %>">
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "articles/multiple_engagements" if FeatureFlag.enabled?(:multiple_reactions) && @article.public_reactions_count? %>
|
||||
|
||||
<h1 class="fs-3xl m:fs-4xl l:fs-5xl fw-bold s:fw-heavy lh-tight mb-2 <%= @article.title_length_classification %>">
|
||||
<% if @article.search_optimized_title_preamble.present? && !user_signed_in? %>
|
||||
<span class="fs-xl color-base-70 block"><%= @article.search_optimized_title_preamble %></span>
|
||||
|
|
|
|||
7
config/initializers/inline_svg.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require "unique_svg_transform"
|
||||
|
||||
InlineSvg.configure do |config|
|
||||
# NOTE: There is currently a bug in the inline_svg gem where custom attributes
|
||||
# are de-facto required in order for a custom transformation to be activated.
|
||||
config.add_custom_transformation attribute: :force_unique, transform: UniqueSvgTransform
|
||||
end
|
||||
|
|
@ -11,6 +11,10 @@ en:
|
|||
thumbsdown: Thumbs down
|
||||
unicorn: Unicorn
|
||||
vomit: Flag
|
||||
lightbulb: Lightbulb
|
||||
raised_hands: Raised hands
|
||||
rocket: Rocket
|
||||
drawer_button: Add reaction
|
||||
summary:
|
||||
count_html:
|
||||
one: "%{count}%{start}\_reaction%{end}"
|
||||
|
|
@ -29,3 +33,6 @@ en:
|
|||
aria_label: Add to reading list
|
||||
icon: Save button
|
||||
text: Save
|
||||
readinglist:
|
||||
title: Save
|
||||
aria_label: Add to reading list
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ fr:
|
|||
thumbsdown: Thumbs down
|
||||
unicorn: Unicorn
|
||||
vomit: Flag
|
||||
lightbulb: Lightbulb
|
||||
raised_hands: Raised hands
|
||||
rocket: Rocket
|
||||
drawer_button: Add reaction
|
||||
summary:
|
||||
count_html:
|
||||
one: "%{count}%{start}\_réaction%{end}"
|
||||
|
|
@ -29,3 +33,8 @@ fr:
|
|||
aria_label: Add to reading list
|
||||
icon: Save button
|
||||
text: Save
|
||||
readinglist:
|
||||
title: Sauver
|
||||
aria_label: Add to reading list
|
||||
icon: Save button
|
||||
text: Save
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
---
|
||||
like:
|
||||
position: 1
|
||||
icon: later
|
||||
readinglist:
|
||||
position: 2
|
||||
icon: later
|
||||
name: Like
|
||||
icon: sparkle-heart
|
||||
unicorn:
|
||||
position: 2
|
||||
name: Unicorn
|
||||
icon: multi-unicorn
|
||||
exploding-head:
|
||||
position: 3
|
||||
icon: later
|
||||
icon: exploding-head
|
||||
raised_hands:
|
||||
position: 4
|
||||
icon: raised-hands
|
||||
fire:
|
||||
position: 5
|
||||
icon: fire
|
||||
readinglist:
|
||||
position: 5
|
||||
icon: save
|
||||
published: false
|
||||
thumbsup:
|
||||
privileged: true
|
||||
score: 5.0
|
||||
|
|
@ -16,6 +28,7 @@ thumbsdown:
|
|||
score: -10.0
|
||||
vomit:
|
||||
privileged: true
|
||||
name: Vomit
|
||||
score: -50.0
|
||||
thinking:
|
||||
published: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
module DataUpdateScripts
|
||||
class AddMultipleReactionsFeatureFlag
|
||||
def run
|
||||
FeatureFlag.add :multiple_reactions
|
||||
end
|
||||
end
|
||||
end
|
||||
35
lib/unique_svg_transform.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
require "securerandom"
|
||||
|
||||
# rubocop:disable Style/StringLiterals
|
||||
|
||||
class UniqueSvgTransform < InlineSvg::CustomTransformation
|
||||
def transform(doc, uniq_str = SecureRandom.hex[0..6])
|
||||
# nodes with id attributes
|
||||
doc.xpath('//*[@id]').each do |node|
|
||||
node_id = node.get_attribute 'id'
|
||||
node.set_attribute 'id', [node_id, uniq_str].join('-')
|
||||
end
|
||||
|
||||
# nodes with id references
|
||||
doc.xpath('//*[@href]').each do |node|
|
||||
node_href = node.get_attribute 'href'
|
||||
node.set_attribute 'href', [node_href, uniq_str].join('-')
|
||||
end
|
||||
|
||||
# nodes with fill references
|
||||
doc.xpath('//*[@fill]').each do |node|
|
||||
node_url = node.get_attribute 'fill'
|
||||
if (md = /url\((.+)\)/.match node_url)
|
||||
id = md[1]
|
||||
end
|
||||
next unless id
|
||||
|
||||
new_url = "url(#{id}-#{uniq_str})"
|
||||
node.set_attribute 'fill', new_url
|
||||
end
|
||||
|
||||
doc
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Style/StringLiterals
|
||||
61
spec/fixtures/files/unicorn-unique.svg
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M13.178 11.2a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#a-87a63a)"/>
|
||||
<path d="M17.518 17.126c-.07.384.106.737.391.788.286.052.574-.216.644-.6.07-.385-.106-.738-.391-.79-.286-.052-.574.217-.644.602Z" fill="url(#b-87a63a)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m18.394 14.92-.214-.461c-1.296.094-2.863.67-3.127 1.992-.112.557-.469 1.028-.924 1.413.028.06.053.117.078.174.113.256.218.492.5.788 1.334 1.4 3.325.74 4.093-.619.62-1.098.196-2.003-.406-3.288Zm-.485 2.996c-.285-.052-.46-.405-.39-.789.069-.385.357-.653.643-.602.285.052.46.405.391.79-.07.383-.358.652-.644.6Z" fill="url(#c-87a63a)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m14.475 6.186.002.001c-.298.265-.288.796.038 1.213.322.412.825.556 1.155.345l-.005.01.027-.023a.112.112 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.003c.178.195.335.395.46.598h.002c.648 1.22.82 2.491.98 3.66.12.893.234 1.727.545 2.433a37.68 37.68 0 0 0 .144.322c-1.296.094-2.863.67-3.127 1.991-.112.558-.469 1.029-.924 1.414-.17-.364-.433-.795-1.25-1.49-1.588-.317-2.563-1.045-2.563-2.552 0 0-.464-.223-1.065 3.505-.156.965-.098 1.93-.036 2.934l.017.294c.063 1.057-.796 1.895-1.855 1.895H3.521a1.969 1.969 0 0 1-1.969-1.969v-7.45 3.902c.464-1.023 1-2.468 1.464-3.714.371-.998.694-1.868.894-2.29 1.643-3.476 4.557-6.5 9.001-5.482a7.492 7.492 0 0 1 1.565.739Zm-1.297 5.012a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#d-87a63a)"/>
|
||||
<path d="m16.686 4.673.034-.064.031-.057c.422-.782-.161-.511-.532-.255-.178.124-.855.382-1.522.621.24.148.49.35.75.57.35-.237.775-.516 1.239-.815Z" fill="url(#e-87a63a)"/>
|
||||
<path d="M13.878 3.958c.292-.127.524-.227.63-.374.678-.928.487.27.415.617a3.887 3.887 0 0 1-.225.717 5.45 5.45 0 0 1-.597 1.05 7.02 7.02 0 0 0-1.19-.522 8.125 8.125 0 0 0-.804-.143c.534-.81 1.249-1.12 1.771-1.345Z" fill="url(#f-87a63a)"/>
|
||||
<path d="M16.049 7.444c.178.195.335.396.46.599 0 0 1.298.328 1.971-.786.067-.11-.043-.236-.17-.222-.458.052-.9-.097-1.322-.344-.5.406-.852.687-.94.753Z" fill="url(#g-87a63a)"/>
|
||||
<path d="M15.447 5.487c-.469.316-.805.554-.93.66-.003.003-.006.004-.01.006l-.009.005-.005.007c-.002.003-.003.006-.006.008l-.009.008-.002.003a8.712 8.712 0 0 0-.375-.217 5.44 5.44 0 0 0 .596-1.049c.24.147.49.349.75.57Z" fill="url(#h-87a63a)"/>
|
||||
<path d="M13.263 4.25a3.192 3.192 0 0 0-1.156 1.053c-3.995-.5-6.655 2.363-8.197 5.625-.2.422-.523 1.292-.894 2.29-.463 1.245-1 2.69-1.463 3.714V9.779c0-1.26.24-2.596 1.032-3.577a5.7 5.7 0 0 1 .96-.933c.117-.09.282.035.244.178-.213.786-.317 1.869-.195 2.64.087-2.288 1.86-3.839 2.552-4.196 1.208-.623 2.4-.58 3.715-.471.113.01.184.142.146.25-.06.171-.11.413-.081.673.28-.51.935-.828 1.36-.741.77.156 1.45.38 1.977.648Z" fill="url(#i-87a63a)"/>
|
||||
<path d="M13.646 5.738c.152-.238.27-.483.37-.693.118-.247.214-.446.316-.532.202-.168-.068-.17-.068-.17-.44.265-.972.662-1.278 1.129.21.07.432.16.66.266Z" fill="url(#j-87a63a)"/>
|
||||
<path d="M14.475 6.187h.002c-.298.266-.288.796.038 1.213.322.412.825.556 1.155.346l-.005.01.027-.023a.115.115 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.004c.11-.084.654-.517 1.38-1.113a2.647 2.647 0 0 1-.634-.613 2.576 2.576 0 0 1-.428-.838l.528-.342c.014.173.1.473.368.831.24.323.479.494.642.57.35-.29.728-.605 1.11-.929a2.775 2.775 0 0 1-.518-.531 2.71 2.71 0 0 1-.385-.707c.17-.106.34-.21.511-.314.044.174.144.408.342.672.18.243.357.397.503.492.36-.309.717-.62 1.05-.92a2.92 2.92 0 0 1-.694-.942l-.005-.014c.176-.101.347-.199.512-.291l.01.027.014.037a2.386 2.386 0 0 0 .566.764l.017.014a.21.21 0 0 1 .015.011c.952-.877 1.619-1.586 1.472-1.733-.417-.416-7.235 4.002-7.909 4.573l-.008.005-.01.006-.005.007-.006.007-.009.008-.003.005Z" fill="url(#k-87a63a)"/>
|
||||
<path d="m19.824 2.761-.005-.014c.176-.102.347-.2.512-.292.01.022.013.04.024.064a2.357 2.357 0 0 0 .566.765l.033.025-.435.394a2.92 2.92 0 0 1-.695-.942ZM18.497 4.48a2.71 2.71 0 0 1-.385-.707l.511-.314c.044.174.144.408.342.672.18.244.358.398.504.493l-.454.388a2.775 2.775 0 0 1-.518-.532Zm-1.702 1.24a2.576 2.576 0 0 1-.428-.838l.528-.342c.014.172.1.472.368.831.24.323.48.494.642.57l-.475.392a2.646 2.646 0 0 1-.635-.613Z" fill="#EF5E3E"/>
|
||||
<defs>
|
||||
<linearGradient id="a-87a63a" x1="13.178" y1="8.949" x2="13.178" y2="11.199" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b-87a63a" x1="18.036" y1="16.518" x2="18.036" y2="17.921" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#061722"/>
|
||||
<stop offset="1" stop-color="#051622"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c-87a63a" x1="16.617" y1="14.459" x2="16.617" y2="19.605" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d-87a63a" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="e-87a63a" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f-87a63a" x1="13.322" y1="3.238" x2="13.322" y2="7.148" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#DBEBF7"/>
|
||||
<stop offset="1" stop-color="#9AB5C8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="g-87a63a" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="h-87a63a" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="i-87a63a" x1="10.09" y1="3.355" x2="10.09" y2="16.932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="j-87a63a" x1="13.581" y1="4.344" x2="13.581" y2="6.288" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="k-87a63a" x1="18.353" y1="1.549" x2="18.353" y2="7.842" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFA271"/>
|
||||
<stop offset="1" stop-color="#F36C24"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
60
spec/fixtures/files/unicorn.svg
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.178 11.2a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#a)"/>
|
||||
<path d="M17.518 17.126c-.07.384.106.737.391.788.286.052.574-.216.644-.6.07-.385-.106-.738-.391-.79-.286-.052-.574.217-.644.602Z" fill="url(#b)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m18.394 14.92-.214-.461c-1.296.094-2.863.67-3.127 1.992-.112.557-.469 1.028-.924 1.413.028.06.053.117.078.174.113.256.218.492.5.788 1.334 1.4 3.325.74 4.093-.619.62-1.098.196-2.003-.406-3.288Zm-.485 2.996c-.285-.052-.46-.405-.39-.789.069-.385.357-.653.643-.602.285.052.46.405.391.79-.07.383-.358.652-.644.6Z" fill="url(#c)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="m14.475 6.186.002.001c-.298.265-.288.796.038 1.213.322.412.825.556 1.155.345l-.005.01.027-.023a.112.112 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.003c.178.195.335.395.46.598h.002c.648 1.22.82 2.491.98 3.66.12.893.234 1.727.545 2.433a37.68 37.68 0 0 0 .144.322c-1.296.094-2.863.67-3.127 1.991-.112.558-.469 1.029-.924 1.414-.17-.364-.433-.795-1.25-1.49-1.588-.317-2.563-1.045-2.563-2.552 0 0-.464-.223-1.065 3.505-.156.965-.098 1.93-.036 2.934l.017.294c.063 1.057-.796 1.895-1.855 1.895H3.521a1.969 1.969 0 0 1-1.969-1.969v-7.45 3.902c.464-1.023 1-2.468 1.464-3.714.371-.998.694-1.868.894-2.29 1.643-3.476 4.557-6.5 9.001-5.482a7.492 7.492 0 0 1 1.565.739Zm-1.297 5.012a1.125 1.125 0 1 1 0-2.25 1.125 1.125 0 0 1 0 2.25Z" fill="url(#d)"/>
|
||||
<path d="m16.686 4.673.034-.064.031-.057c.422-.782-.161-.511-.532-.255-.178.124-.855.382-1.522.621.24.148.49.35.75.57.35-.237.775-.516 1.239-.815Z" fill="url(#e)"/>
|
||||
<path d="M13.878 3.958c.292-.127.524-.227.63-.374.678-.928.487.27.415.617a3.887 3.887 0 0 1-.225.717 5.45 5.45 0 0 1-.597 1.05 7.02 7.02 0 0 0-1.19-.522 8.125 8.125 0 0 0-.804-.143c.534-.81 1.249-1.12 1.771-1.345Z" fill="url(#f)"/>
|
||||
<path d="M16.049 7.444c.178.195.335.396.46.599 0 0 1.298.328 1.971-.786.067-.11-.043-.236-.17-.222-.458.052-.9-.097-1.322-.344-.5.406-.852.687-.94.753Z" fill="url(#g)"/>
|
||||
<path d="M15.447 5.487c-.469.316-.805.554-.93.66-.003.003-.006.004-.01.006l-.009.005-.005.007c-.002.003-.003.006-.006.008l-.009.008-.002.003a8.712 8.712 0 0 0-.375-.217 5.44 5.44 0 0 0 .596-1.049c.24.147.49.349.75.57Z" fill="url(#h)"/>
|
||||
<path d="M13.263 4.25a3.192 3.192 0 0 0-1.156 1.053c-3.995-.5-6.655 2.363-8.197 5.625-.2.422-.523 1.292-.894 2.29-.463 1.245-1 2.69-1.463 3.714V9.779c0-1.26.24-2.596 1.032-3.577a5.7 5.7 0 0 1 .96-.933c.117-.09.282.035.244.178-.213.786-.317 1.869-.195 2.64.087-2.288 1.86-3.839 2.552-4.196 1.208-.623 2.4-.58 3.715-.471.113.01.184.142.146.25-.06.171-.11.413-.081.673.28-.51.935-.828 1.36-.741.77.156 1.45.38 1.977.648Z" fill="url(#i)"/>
|
||||
<path d="M13.646 5.738c.152-.238.27-.483.37-.693.118-.247.214-.446.316-.532.202-.168-.068-.17-.068-.17-.44.265-.972.662-1.278 1.129.21.07.432.16.66.266Z" fill="url(#j)"/>
|
||||
<path d="M14.475 6.187h.002c-.298.266-.288.796.038 1.213.322.412.825.556 1.155.346l-.005.01.027-.023a.115.115 0 0 1 .01-.006.081.081 0 0 0 .014-.008.022.022 0 0 0 .006-.007.027.027 0 0 1 .005-.007l.316-.256.006-.004c.11-.084.654-.517 1.38-1.113a2.647 2.647 0 0 1-.634-.613 2.576 2.576 0 0 1-.428-.838l.528-.342c.014.173.1.473.368.831.24.323.479.494.642.57.35-.29.728-.605 1.11-.929a2.775 2.775 0 0 1-.518-.531 2.71 2.71 0 0 1-.385-.707c.17-.106.34-.21.511-.314.044.174.144.408.342.672.18.243.357.397.503.492.36-.309.717-.62 1.05-.92a2.92 2.92 0 0 1-.694-.942l-.005-.014c.176-.101.347-.199.512-.291l.01.027.014.037a2.386 2.386 0 0 0 .566.764l.017.014a.21.21 0 0 1 .015.011c.952-.877 1.619-1.586 1.472-1.733-.417-.416-7.235 4.002-7.909 4.573l-.008.005-.01.006-.005.007-.006.007-.009.008-.003.005Z" fill="url(#k)"/>
|
||||
<path d="m19.824 2.761-.005-.014c.176-.102.347-.2.512-.292.01.022.013.04.024.064a2.357 2.357 0 0 0 .566.765l.033.025-.435.394a2.92 2.92 0 0 1-.695-.942ZM18.497 4.48a2.71 2.71 0 0 1-.385-.707l.511-.314c.044.174.144.408.342.672.18.244.358.398.504.493l-.454.388a2.775 2.775 0 0 1-.518-.532Zm-1.702 1.24a2.576 2.576 0 0 1-.428-.838l.528-.342c.014.172.1.472.368.831.24.323.48.494.642.57l-.475.392a2.646 2.646 0 0 1-.635-.613Z" fill="#EF5E3E"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="13.178" y1="8.949" x2="13.178" y2="11.199" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="18.036" y1="16.518" x2="18.036" y2="17.921" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#061722"/>
|
||||
<stop offset="1" stop-color="#051622"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="16.617" y1="14.459" x2="16.617" y2="19.605" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="e" x1="10.329" y1="4.035" x2="10.329" y2="22.449" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E3F3FF"/>
|
||||
<stop offset="1" stop-color="#749CC4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f" x1="13.322" y1="3.238" x2="13.322" y2="7.148" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#DBEBF7"/>
|
||||
<stop offset="1" stop-color="#9AB5C8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="g" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="h" x1="10.09" y1="3.355" x2="10.09" y2="16.931" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="i" x1="10.09" y1="3.355" x2="10.09" y2="16.932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#60379A"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="j" x1="13.581" y1="4.344" x2="13.581" y2="6.288" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3A6481"/>
|
||||
<stop offset="1" stop-color="#1E445E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="k" x1="18.353" y1="1.549" x2="18.353" y2="7.842" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFA271"/>
|
||||
<stop offset="1" stop-color="#F36C24"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20221220131148_add_multiple_reactions_feature_flag.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::AddMultipleReactionsFeatureFlag do
|
||||
before do
|
||||
allow(FeatureFlag).to receive(:add)
|
||||
end
|
||||
|
||||
it "adds the feature flag" do
|
||||
described_class.new.run
|
||||
expect(FeatureFlag).to have_received(:add).with(:multiple_reactions)
|
||||
end
|
||||
end
|
||||
15
spec/lib/unique_svg_transform_spec.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
require "rails_helper"
|
||||
|
||||
require "unique_svg_transform"
|
||||
|
||||
RSpec.describe UniqueSvgTransform do
|
||||
let(:input) { "#{fixture_path}/files/unicorn.svg" }
|
||||
let(:expectation) { File.read("#{fixture_path}/files/unicorn-unique.svg") }
|
||||
let(:pseudo_unique) { "87a63a" }
|
||||
|
||||
it "transforms input doc into uniquely identified SVG" do
|
||||
doc = Nokogiri::XML(File.open(input))
|
||||
output = described_class.new(true).transform(doc, pseudo_unique).to_xml
|
||||
expect(output).to eq expectation
|
||||
end
|
||||
end
|
||||
|
|
@ -1341,4 +1341,20 @@ RSpec.describe Article do
|
|||
expect(another_article.errors.messages[:feed_source_url]).to include(error_message)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reaction_categories reports unique associated reaction categories" do
|
||||
before do
|
||||
user2 = create(:user)
|
||||
user2.add_role(:trusted)
|
||||
|
||||
create(:reaction, reactable: article, category: "like")
|
||||
create(:reaction, reactable: article, category: "like")
|
||||
create(:reaction, reactable: article, category: "readinglist")
|
||||
create(:reaction, reactable: article, category: "vomit", user: user2)
|
||||
end
|
||||
|
||||
it "reports accurately" do
|
||||
expect(article.reaction_categories).to contain_exactly("like", "readinglist", "vomit")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe ReactionCategory, type: :model do
|
||||
RSpec.describe ReactionCategory do
|
||||
let(:attributes_hash) do
|
||||
{
|
||||
"slug" => "lol",
|
||||
|
|
@ -17,12 +17,13 @@ RSpec.describe ReactionCategory, type: :model do
|
|||
end
|
||||
|
||||
it "lists all category slugs" do
|
||||
expect(described_class.all_slugs).to contain_exactly(*%i[like unicorn readinglist hands thinking thumbsup
|
||||
thumbsdown vomit])
|
||||
expect(described_class.all_slugs).to contain_exactly(*%i[exploding-head fire hands like
|
||||
raised_hands readinglist thinking
|
||||
thumbsdown thumbsup unicorn vomit])
|
||||
end
|
||||
|
||||
it "lists public categories" do
|
||||
expect(described_class.public).to contain_exactly(*%i[like readinglist unicorn])
|
||||
expect(described_class.public).to contain_exactly(*%i[like unicorn raised_hands fire exploding-head])
|
||||
end
|
||||
|
||||
it "lists privileged categories" do
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ RSpec.describe Reaction do
|
|||
|
||||
expected_result = [
|
||||
{ category: "like", count: 1 },
|
||||
{ category: "readinglist", count: 0 },
|
||||
{ category: "unicorn", count: 1 },
|
||||
{ category: "readinglist", count: 0 },
|
||||
]
|
||||
expect(described_class.count_for_article(article.id)).to eq(expected_result)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ RSpec.describe "NotificationsIndex" do
|
|||
end
|
||||
|
||||
it "does group notifications that are on different days but have the same reactable" do
|
||||
mock_heart_reaction_notifications(2, %w[unicorn like readinglist])
|
||||
mock_heart_reaction_notifications(2, %w[unicorn like])
|
||||
Notification.last.update(created_at: Notification.last.created_at - 1.day)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
|
|
@ -223,8 +223,8 @@ RSpec.describe "NotificationsIndex" do
|
|||
end
|
||||
|
||||
it "does not group notifications that are on the same day but have different reactables" do
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like readinglist], article1)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like readinglist], article2)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like], article1)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like], article2)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 2
|
||||
|
|
@ -294,7 +294,7 @@ RSpec.describe "NotificationsIndex" do
|
|||
end
|
||||
|
||||
it "does group notifications that are on different days but have the same reactable" do
|
||||
mock_heart_reaction_notifications(2, %w[unicorn like readinglist])
|
||||
mock_heart_reaction_notifications(2, %w[unicorn like])
|
||||
Notification.last.update(created_at: Notification.last.created_at - 1.day)
|
||||
|
||||
get notifications_path(filter: :org, org_id: organization.id)
|
||||
|
|
@ -303,8 +303,8 @@ RSpec.describe "NotificationsIndex" do
|
|||
end
|
||||
|
||||
it "does not group notifications that are on the same day but have different reactables" do
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like readinglist], article1)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like readinglist], article2)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like], article1)
|
||||
mock_heart_reaction_notifications(1, %w[unicorn like], article2)
|
||||
|
||||
get notifications_path(filter: :org, org_id: organization.id)
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RSpec.describe "Reactions" do
|
|||
{ "category" => "readinglist", "count" => 0 },
|
||||
{ "category" => "unicorn", "count" => 0 },
|
||||
]
|
||||
expect(result["article_reaction_counts"]).to eq(expected_reactions_counts)
|
||||
expect(result["article_reaction_counts"]).to contain_exactly(*expected_reactions_counts)
|
||||
expect(result["reactions"].to_json).to eq(user.reactions.where(reactable: article).to_json)
|
||||
end
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ RSpec.describe "Reactions" do
|
|||
{ "category" => "readinglist", "count" => 0 },
|
||||
{ "category" => "unicorn", "count" => 0 },
|
||||
]
|
||||
expect(result["article_reaction_counts"]).to eq(expected_reactions)
|
||||
expect(result["article_reaction_counts"]).to contain_exactly(*expected_reactions)
|
||||
expect(result["reactions"]).to be_empty
|
||||
end
|
||||
|
||||
|
|
|
|||