Merge remote-tracking branch 'origin' into mobile-panorama
3
.github/workflows/auth-crowdin-sync.yml
vendored
@@ -2,8 +2,9 @@ name: "Sync Crowdin translations (auth)"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run Mondays at ~6:30 AM IST
|
||||
# See: [Note: Run workflow on specific days of the week]
|
||||
- cron: "50 1 * * 2"
|
||||
- cron: "50 0 * * 1"
|
||||
# Also allow manually running the workflow.
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
3
.github/workflows/mobile-crowdin-sync.yml
vendored
@@ -2,8 +2,9 @@ name: "Sync Crowdin translations (mobile)"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run Mondays at ~6:30 AM IST
|
||||
# See: [Note: Run workflow on specific days of the week]
|
||||
- cron: "40 1 * * 2"
|
||||
- cron: "40 0 * * 1"
|
||||
# Also allow manually running the workflow.
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
4
.github/workflows/web-crowdin-sync.yml
vendored
@@ -9,6 +9,8 @@ on:
|
||||
# Or the workflow itself is changed
|
||||
- ".github/workflows/web-crowdin.yml"
|
||||
schedule:
|
||||
# Run Mondays at ~6:00 AM IST
|
||||
#
|
||||
# [Note: Run workflow on specific days of the week]
|
||||
#
|
||||
# The last (5th) component of the cron syntax denotes the day of the
|
||||
@@ -16,7 +18,7 @@ on:
|
||||
# and FRI, this can be set to `2,5`.
|
||||
#
|
||||
# See also: [Note: Run workflow every 24 hours]
|
||||
- cron: "20 1 * * 2"
|
||||
- cron: "20 0 * * 1"
|
||||
# Also allow manually running the workflow.
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"icons": [
|
||||
{ "title": "1xBet",
|
||||
"altNames": ["1x", "1x bet", "1x-bet"
|
||||
]
|
||||
},
|
||||
{ "title": "1xBet",
|
||||
"altNames": ["1x", "1x bet", "1x-bet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "3Commas"
|
||||
},
|
||||
@@ -14,6 +14,10 @@
|
||||
{
|
||||
"title": "Airtable"
|
||||
},
|
||||
{
|
||||
"title": "airtm",
|
||||
"hex": "000000"
|
||||
},
|
||||
{
|
||||
"title": "Anycoin Direct",
|
||||
"slug": "anycoindirect"
|
||||
@@ -27,6 +31,9 @@
|
||||
{
|
||||
"title": "bitget"
|
||||
},
|
||||
{
|
||||
"title": "Bitmart"
|
||||
},
|
||||
{
|
||||
"title": "BitMEX"
|
||||
},
|
||||
@@ -113,6 +120,9 @@
|
||||
{
|
||||
"title": "DEGIRO"
|
||||
},
|
||||
{
|
||||
"title": "deriv"
|
||||
},
|
||||
{
|
||||
"title": "DirectAdmin"
|
||||
},
|
||||
@@ -182,6 +192,9 @@
|
||||
{
|
||||
"title": "Hivelocity"
|
||||
},
|
||||
{
|
||||
"title": "HTX"
|
||||
},
|
||||
{
|
||||
"title": "IceDrive",
|
||||
"slug": "Icedrive"
|
||||
@@ -328,8 +341,8 @@
|
||||
{
|
||||
"title": "Odido"
|
||||
},
|
||||
{ "title": "okx",
|
||||
"hex": "858585" },
|
||||
{ "title": "okx",
|
||||
"hex": "858585" },
|
||||
{
|
||||
"title": "Parsec"
|
||||
},
|
||||
@@ -547,8 +560,8 @@
|
||||
],
|
||||
"slug": "Yandex"
|
||||
},
|
||||
{ "title": "yahoo" },
|
||||
{
|
||||
{ "title": "yahoo" },
|
||||
|
||||
{
|
||||
"title": "YNAB",
|
||||
"altNames": [
|
||||
|
||||
1
auth/assets/custom-icons/icons/airtm.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-738.2 149.1 435.4 102.8" xml:space="preserve"><path d="M-612.2 149.1h-18.4v99.6h18.4zm-104.1 79 25.4-55 14 30.2zm79.3 20.7-17.9-38.7 17.4-11-8.9-15-15.9 10-17.4-37.6c-1.1-2.3-2.7-4.2-4.7-5.5s-4.4-2-6.7-1.9c-2.4.1-4.7.9-6.6 2.3s-3.4 3.3-4.3 5.5l-34.9 75.5-.1.2c-2.3 5.7-.8 12.2 3.8 16.2 4.5 3.9 10.8 4.2 15.6.9l48.2-30.3 13.7 29.5zm54.3-53.1h27.3c14.1 0 18.8-7.7 18.8-16.4s-4.7-15.9-18.7-15.9h-27.4zm30.9-46.6c26.2 0 34.6 14.6 34.6 30 0 11.6-5.8 23.7-21.5 27.9l24.7 41.7h-20.8l-23-39.1h-24.9v39.1H-601v-99.6zm40.1 15.4h35v84.2h18.4v-84.2h35v-15.4h-88.4zm123.9-15.4H-415v99.6h16.6v-72.1c0-4.5-.3-8.6-.7-12.1h.5c.5 2.7 1.9 7.9 3.2 11.6l27.3 72.5h17.3l27.3-72.5c1.5-3.8 3.1-9.4 3.5-11.6h.5c-.4 3.6-.7 8-.7 12.1v72.1h17.2v-99.6h-27.4l-25.8 69.5c-1.5 4.2-2.3 6.7-2.8 10.1h-.3c-.5-3-1.2-5.2-2.3-8.7-.1-.4-.3-.9-.4-1.4z" style="fill:#050505"/></svg>
|
||||
|
After Width: | Height: | Size: 892 B |
@@ -1,18 +1 @@
|
||||
<svg width="512" height="155" viewBox="0 0 512 155" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3037_70295)">
|
||||
<path d="M200.682 74.9284C204.533 72.7411 207.619 70.0177 209.865 66.8064C212.425 63.1502 213.725 58.974 213.725 54.4011C213.725 45.8396 210.524 39.1571 204.217 34.5359C197.985 29.9683 189.41 27.655 178.723 27.655H140.078V126.826H180.267C191.522 126.826 200.534 124.209 207.05 119.044C213.618 113.836 216.952 106.542 216.952 97.3615C216.952 91.7726 215.384 86.9638 212.296 83.0717C209.525 79.5924 205.624 76.8556 200.684 74.9284H200.682ZM160.991 45.5286H177.042C182.076 45.5286 185.877 46.5043 188.346 48.4236C190.785 50.3187 191.97 52.8089 191.97 56.0335C191.97 59.2581 190.785 61.8743 188.346 63.7775C185.877 65.6967 182.076 66.6724 177.042 66.6724H160.991V45.5286ZM191.209 105.591C188.426 107.821 184.178 108.949 178.584 108.949H160.991V84.68H178.584C184.175 84.68 188.429 85.771 191.222 87.9127C193.991 90.0491 195.337 92.8689 195.337 96.5439C195.337 100.409 193.986 103.368 191.209 105.593V105.591Z" fill="#03AAC1"/>
|
||||
<path d="M245.205 52.6685H224.152V126.822H245.205V52.6685Z" fill="#03AAC1"/>
|
||||
<path d="M453.137 63.8202C449.805 59.8531 445.629 56.7437 440.724 54.5751C435.824 52.4093 430.313 51.3049 424.351 51.3049C417.044 51.3049 410.453 52.9642 404.754 56.2344C399.042 59.5153 394.509 64.0722 391.277 69.787C388.044 75.4965 386.406 82.1227 386.406 89.4753C386.406 96.8279 387.996 103.891 391.129 109.689C394.271 115.506 398.787 120.068 404.551 123.26C410.303 126.434 417.06 128.045 424.633 128.045C433.929 128.045 441.82 125.708 448.093 121.094C454.134 116.656 458.289 110.394 460.552 102.556H439.357C438.242 105.223 436.671 107.494 434.422 109.094C431.763 110.981 428.329 111.938 424.212 111.938C420.642 111.938 417.583 111.069 415.12 109.359C412.646 107.649 410.756 105.121 409.515 101.838C408.77 99.8864 408.242 97.6428 407.931 95.1472H460.922L461.026 94.4878C461.972 88.5076 461.734 82.841 460.308 77.6462C458.879 72.438 456.467 67.7873 453.137 63.8202ZM408.266 81.4177C408.59 79.9086 409.027 78.3378 409.582 76.9064C410.874 73.5799 412.739 71.02 415.13 69.3099C417.511 67.5997 420.473 66.7366 423.936 66.7366C428.276 66.7366 431.838 68.1438 434.524 70.9128C437.07 73.5477 438.515 77.1476 438.823 81.4177H408.268H408.266Z" fill="#03AAC1"/>
|
||||
<path d="M245.635 27.6521H223.703V45.5257H245.635V27.6521Z" fill="#03AAC1"/>
|
||||
<path d="M360.93 61.0807C358.44 58.0893 355.521 55.7706 352.229 54.1677C348.3 52.2672 343.702 51.3049 338.564 51.3049C332.273 51.3049 326.633 52.8569 321.808 55.9127C316.989 58.9658 313.166 63.2814 310.451 68.7309C307.746 74.1669 306.371 80.5492 306.371 87.7088C306.371 94.8684 307.816 100.875 310.668 106.349C313.525 111.841 317.487 116.216 322.441 119.358C327.405 122.507 333.066 124.105 339.269 124.105C343.952 124.105 348.23 123.091 351.977 121.089C354.944 119.505 357.429 117.409 359.389 114.849V122.923C359.389 127.927 358.073 131.768 355.486 134.328C352.891 136.888 348.946 138.18 343.756 138.18C339.285 138.18 335.768 137.167 333.307 135.164C331.294 133.527 329.536 131.377 329.126 126.825H308.829C309.017 133.194 310.888 137.486 313.528 141.246C316.49 145.46 320.656 148.755 325.92 151.03C331.134 153.279 337.183 154.421 343.898 154.421C355.17 154.421 364.144 151.588 370.564 146.007C377.024 140.394 380.299 132.535 380.299 122.652V52.6666H360.93V61.0807ZM357.223 97.5597C355.888 100.315 354.004 102.476 351.615 103.971C349.23 105.467 346.351 106.228 343.054 106.228C338.288 106.228 334.583 104.607 331.721 101.267C328.869 97.9457 327.424 93.3835 327.424 87.7062C327.424 82.0289 328.869 77.4666 331.721 74.1401C334.546 70.8458 338.36 69.1785 343.054 69.1785C347.747 69.1785 351.894 70.8699 354.81 74.2152C357.754 77.5873 359.247 82.1736 359.247 87.8375C359.247 91.5071 358.566 94.7746 357.223 97.557V97.5597Z" fill="#03AAC1"/>
|
||||
<path d="M512.002 70.4034V52.6692H496.806V27.6521H475.893V52.6692H462.914V70.4034H475.893V99.4333C475.893 115.573 479.072 127.434 498.889 127.067L511.329 126.823V110.195H505.156C496.348 110.195 496.839 105.06 496.839 95.5224L496.804 70.4034H511.999H512.002Z" fill="#03AAC1"/>
|
||||
<path d="M301.256 70.4034V52.6692H286.06V27.6521H265.147V52.6692H252.168V70.4034H265.147V99.4333C265.147 115.573 268.326 127.434 288.143 127.067L300.583 126.823V110.195H294.41C285.602 110.195 286.092 105.06 286.092 95.5224L286.058 70.4034H301.253H301.256Z" fill="#03AAC1"/>
|
||||
<path d="M52.4882 45.5261H84.0671L116.373 77.6251C118.474 79.7132 118.485 83.1094 116.394 85.2083L74.9642 126.847H42.4336L52.2684 117.286L88.3774 81.4046L52.7267 45.5234" fill="#03AAC1"/>
|
||||
<path d="M65.4673 81.325H33.8883L1.58286 49.226C-0.518654 47.1378 -0.529376 43.7416 1.56142 41.6428L42.9912 0.0012207H75.5218L65.6871 9.56257L29.578 45.4438L65.2287 81.325" fill="#03AAC1"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3037_70295">
|
||||
<rect width="512" height="154.416" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="40" height="40" rx="8.711" fill="#00f0ff"/><path d="M18.46 15.767h7.468l7.64 7.592c.498.493.5 1.297.006 1.793L23.775 35h-7.693l2.326-2.261 8.54-8.486-8.432-8.487" fill="#1b1b1b"/><path d="M21.53 24.234h-7.47l-7.64-7.592a1.267 1.267 0 0 1-.005-1.793L16.214 5h7.693l-2.326 2.261-8.54 8.486 8.432 8.487" fill="#1b1b1b"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 410 B |
6
auth/assets/custom-icons/icons/bitmart.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" class="logo" viewBox="0 0 500 500">
|
||||
<g fill="#fff">
|
||||
<path d="M307.382 358.988H153.698v13.428h153.684v-13.428zm-115.22 83.526H76.885v13.43h115.277v-13.43zm-76.984-83.468h-76.7v13.428h76.7v-13.428zm192.204-38.864H192.105v13.428h115.277v-13.428zm-153.797 0H76.942v13.428h76.643v-13.428zM307.38 146.585H115.236v13.428H307.38v-13.428zm-153.795-44.664h-76.7v13.428h76.7v-13.428z"/>
|
||||
<path d="M409.287 231.022v-.34A153.91 153.91 0 0 0 276.144.3h-160.91v19.232H276.6a134.58 134.58 0 0 1 106.653 53.207 134.574 134.574 0 0 1 22.564 117.033 117.661 117.661 0 0 0-63.274-132.566A117.668 117.668 0 0 0 291.45 45.42H153.7v19.232h137.92a98.265 98.265 0 0 1 93.597 128.078 76.986 76.986 0 0 0-13.255-66.509 76.985 76.985 0 0 0-60.713-30.218H191.764v19.402H310.68a57.81 57.81 0 0 1 50.186 86.316 154.248 154.248 0 0 0-53.714-9.673H153.528v19.23h153.625a134.624 134.624 0 0 1 132.234 109.474 115.392 115.392 0 0 0-112.32-89.559H115.179v19.062h212.175a96.05 96.05 0 0 1 62.678 23.55 96.055 96.055 0 0 1 32.058 58.781 76.87 76.87 0 0 0-75.505-62.986H115.292v19.233h231.236a57.637 57.637 0 1 1 0 115.276H76.885v19.232h269.7a76.872 76.872 0 0 0 75.504-62.988 96.047 96.047 0 0 1-95.021 82.163H230.34v19.23h96.727a115.44 115.44 0 0 0 110.838-83.697A134.673 134.673 0 0 1 307.44 480.466H115.463V499.7h191.69a153.629 153.629 0 0 0 149.901-190.401 153.642 153.642 0 0 0-47.482-78.388l-.285.112z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 500 500">
|
||||
<path fill="#182954" d="m75.705 269.386 12.606 10.812a40.902 40.902 0 0 1-8.642 8.853 53.365 53.365 0 0 1-13.599 7.73 45.769 45.769 0 0 1-16.998 3.094 49.02 49.02 0 0 1-25.212-6.466A45.84 45.84 0 0 1 6.72 275.84a50.83 50.83 0 0 1-6.212-25.287 52.621 52.621 0 0 1 3.525-19.394 49.28 49.28 0 0 1 10.2-16.022 46.603 46.603 0 0 1 15.44-10.812 49.626 49.626 0 0 1 19.969-3.938 45.9 45.9 0 0 1 23.51 5.48A49.016 49.016 0 0 1 88.308 219.5l-12.744 11.244A39.368 39.368 0 0 0 64.938 220.2a27.358 27.358 0 0 0-15.296-3.933 27.636 27.636 0 0 0-16.147 4.632 30.695 30.695 0 0 0-10.478 12.508 38.957 38.957 0 0 0-3.688 16.879 36.724 36.724 0 0 0 3.684 16.442 29.719 29.719 0 0 0 10.184 11.793 27.208 27.208 0 0 0 15.44 4.358c4.608.197 9.203-.62 13.456-2.391a27.765 27.765 0 0 0 8.214-5.622l5.381-5.481M93.275 264.047a35.477 35.477 0 0 1 4.535-17.71 34.84 34.84 0 0 1 12.748-12.929 39.497 39.497 0 0 1 18.838-4.778 39.497 39.497 0 0 1 18.838 4.778 34.846 34.846 0 0 1 12.749 12.928 36.889 36.889 0 0 1 4.532 17.709 36.891 36.891 0 0 1-4.532 17.708 36.519 36.519 0 0 1-13.365 13.153 36.875 36.875 0 0 1-18.181 4.837 36.88 36.88 0 0 1-18.203-4.756 36.513 36.513 0 0 1-13.424-13.092 35.479 35.479 0 0 1-4.535-17.707v-.141zm35.979 21.224a16.949 16.949 0 0 0 10.623-3.23c2.804-2.121 5-4.93 6.375-8.151a24.848 24.848 0 0 0 2.124-9.698 24.293 24.293 0 0 0-2.124-9.697 20.265 20.265 0 0 0-6.375-8.15 19.056 19.056 0 0 0-10.623-3.233 19.057 19.057 0 0 0-10.625 3.233 20.118 20.118 0 0 0-6.231 8.009 24.296 24.296 0 0 0-2.125 9.697 24.713 24.713 0 0 0 2.125 9.839 19.985 19.985 0 0 0 6.374 8.15 16.949 16.949 0 0 0 10.624 3.231M168.905 202.628h16.856v17.71h-16.856v-17.71zm0 28.11h16.856v66.758h-16.856v-66.758zM192.416 297.495V230.88h16.147l.42 7.589a35.937 35.937 0 0 1 7.505-5.905 23.656 23.656 0 0 1 12.749-3.094 24.38 24.38 0 0 1 10.396 1.612 24.22 24.22 0 0 1 8.726 5.836 29.047 29.047 0 0 1 6.66 20.097v40.477H238.02v-40.335a13.257 13.257 0 0 0-.76-5.278 13.337 13.337 0 0 0-2.78-4.561 12.19 12.19 0 0 0-4.164-2.694 12.27 12.27 0 0 0-4.902-.82 14.974 14.974 0 0 0-6.377 1.24 14.87 14.87 0 0 0-5.236 3.82 18.046 18.046 0 0 0-4.534 12.51v36.118l-16.851.004z"/>
|
||||
<path fill="#FA4A29" d="m463.25 246.618 29.754-44.007h-28.187l-15.44 24.596-15.883-24.596h-31.163l1.416 1.967-.993-.416a63.329 63.329 0 0 0-23.083-4.046 50.453 50.453 0 0 0-25.92 6.607 46.609 46.609 0 0 0-14.308 12.929 40.334 40.334 0 0 0-15.582-11.806 65.028 65.028 0 0 0-26.344-5.077h-36.686v94.727h36.544a64.026 64.026 0 0 0 26.344-5.202A41.612 41.612 0 0 0 339.3 280.63c3.87 5.299 8.846 9.709 14.59 12.928a51.44 51.44 0 0 0 25.777 6.325 55.023 55.023 0 0 0 24.646-5.34l-1.982 2.953h27.76l18.558-29.108 19.122 29.108h31.73l-36.252-50.878zm-147.452 21.624a25.772 25.772 0 0 1-8.902 5.504 25.916 25.916 0 0 1-10.376 1.523h-10.334v-50.573h10.338c3.62-.305 7.264.165 10.685 1.378a25.427 25.427 0 0 1 9.147 5.65 26.146 26.146 0 0 1 6.374 18.271 24.821 24.821 0 0 1-1.597 9.836 24.965 24.965 0 0 1-5.343 8.436l.008-.025zm101.549 6.911-12.04-11.228a38.572 38.572 0 0 1-10.197 9.149 27.09 27.09 0 0 1-13.6 2.952 25.509 25.509 0 0 1-13.314-3.372 22.838 22.838 0 0 1-8.8-9.415 29.459 29.459 0 0 1-3.118-13.63c-.091-4.623.929-9.2 2.975-13.353a23.258 23.258 0 0 1 8.642-9.415 25.653 25.653 0 0 1 13.738-3.513 24.798 24.798 0 0 1 12.748 3.23 32.061 32.061 0 0 1 9.639 8.733l12.606-12.508 18.415 26.28-17.694 26.09z"/>
|
||||
<path fill="#182954" d="m75.006 269.464 12.657 10.855a41.066 41.066 0 0 1-8.676 8.889 53.579 53.579 0 0 1-13.654 7.76 45.952 45.952 0 0 1-17.066 3.107 49.217 49.217 0 0 1-25.313-6.492 46.023 46.023 0 0 1-17.21-17.64A51.033 51.033 0 0 1-.49 250.556a52.832 52.832 0 0 1 3.538-19.472 49.477 49.477 0 0 1 10.242-16.086 46.79 46.79 0 0 1 15.5-10.855 49.825 49.825 0 0 1 20.05-3.954 46.083 46.083 0 0 1 23.605 5.503 49.212 49.212 0 0 1 15.215 13.685l-12.795 11.29a39.526 39.526 0 0 0-10.667-10.584 27.467 27.467 0 0 0-15.358-3.95 27.747 27.747 0 0 0-16.212 4.651 30.818 30.818 0 0 0-10.52 12.559 39.113 39.113 0 0 0-3.702 16.946 36.87 36.87 0 0 0 3.699 16.508 29.838 29.838 0 0 0 10.224 11.84 27.317 27.317 0 0 0 15.501 4.376 31.64 31.64 0 0 0 13.511-2.401 27.877 27.877 0 0 0 8.247-5.644l5.403-5.503m17.656-5.362a35.619 35.619 0 0 1 4.553-17.781 34.98 34.98 0 0 1 12.8-12.98 39.655 39.655 0 0 1 18.913-4.797 39.66 39.66 0 0 1 18.913 4.797 34.985 34.985 0 0 1 12.8 12.98 37.037 37.037 0 0 1 4.55 17.779 37.04 37.04 0 0 1-4.55 17.78 36.665 36.665 0 0 1-13.418 13.205 37.023 37.023 0 0 1-18.254 4.856 37.027 37.027 0 0 1-18.276-4.774A36.659 36.659 0 0 1 97.2 282.022a35.62 35.62 0 0 1-4.553-17.777v-.142zm36.123 21.31a17.017 17.017 0 0 0 10.666-3.244 20.199 20.199 0 0 0 6.4-8.184 24.947 24.947 0 0 0 2.133-9.736 24.391 24.391 0 0 0-2.133-9.736 20.347 20.347 0 0 0-6.4-8.183 19.133 19.133 0 0 0-10.666-3.245c-3.803 0-7.518 1.13-10.667 3.245a20.2 20.2 0 0 0-6.256 8.041 24.393 24.393 0 0 0-2.134 9.736 24.812 24.812 0 0 0 2.134 9.878 20.065 20.065 0 0 0 6.4 8.184 17.017 17.017 0 0 0 10.666 3.244m39.667-82.975h16.924v17.781H168.58v-17.781zm0 28.223h16.924v67.026H168.58V230.66zm23.605 67.025v-66.884h16.212l.42 7.62a36.08 36.08 0 0 1 7.537-5.929 23.751 23.751 0 0 1 12.8-3.106c3.556-.227 7.12.326 10.438 1.618a24.317 24.317 0 0 1 8.76 5.86 29.164 29.164 0 0 1 6.687 20.177v40.64h-17.067v-40.498a13.31 13.31 0 0 0-.763-5.298 13.39 13.39 0 0 0-2.792-4.58 12.239 12.239 0 0 0-4.18-2.705 12.32 12.32 0 0 0-4.921-.823c-2.2-.058-4.388.367-6.403 1.245a14.929 14.929 0 0 0-5.257 3.836 18.118 18.118 0 0 0-4.552 12.559v36.264l-16.919.004z"/>
|
||||
<path fill="#FA4A29" d="m464.104 246.605 29.874-44.184h-28.3l-15.502 24.695-15.946-24.695h-31.288l1.421 1.975-.997-.418a63.582 63.582 0 0 0-23.175-4.062 50.655 50.655 0 0 0-26.025 6.634 46.796 46.796 0 0 0-14.365 12.98 40.496 40.496 0 0 0-15.644-11.853 65.288 65.288 0 0 0-26.45-5.098h-36.833v95.107h36.691a64.283 64.283 0 0 0 26.45-5.223 41.778 41.778 0 0 0 15.643-11.71 45.958 45.958 0 0 0 14.647 12.98 51.646 51.646 0 0 0 25.882 6.35 55.244 55.244 0 0 0 24.744-5.361l-1.99 2.964h27.872l18.631-29.225 19.2 29.225H500.5l-36.396-51.081zm-148.042 21.71a25.875 25.875 0 0 1-8.938 5.526 26.02 26.02 0 0 1-10.417 1.53H286.33v-50.777h10.38a25.677 25.677 0 0 1 10.727 1.384 25.529 25.529 0 0 1 9.184 5.672 26.25 26.25 0 0 1 6.4 18.345 24.92 24.92 0 0 1-1.604 9.876 25.065 25.065 0 0 1-5.365 8.469l.009-.025zm101.955 6.939L405.93 263.98a38.727 38.727 0 0 1-10.237 9.185 27.198 27.198 0 0 1-13.654 2.965 25.611 25.611 0 0 1-13.368-3.386 22.93 22.93 0 0 1-8.836-9.453 29.577 29.577 0 0 1-3.13-13.685 29.036 29.036 0 0 1 2.987-13.406 23.352 23.352 0 0 1 8.676-9.452 25.756 25.756 0 0 1 13.793-3.528 24.898 24.898 0 0 1 12.8 3.244 32.19 32.19 0 0 1 9.677 8.767l12.657-12.558 18.488 26.386-17.765 26.194z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.3 KiB |
1
auth/assets/custom-icons/icons/deriv.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 216.86 72"><path fill="#FF444F" d="m43.472 2.271-3.715 21.06H26.86c-12.03 0-23.498 9.744-25.623 21.77l-.9 5.118C-1.777 62.246 6.25 71.99 18.281 71.99h10.755c8.768 0 17.122-7.095 18.664-15.86L57.605 0zm-9.15 51.872c-.475 2.707-2.914 4.911-5.622 4.911h-6.534c-5.408 0-9.026-4.388-8.077-9.803l.564-3.192c.958-5.405 6.119-9.803 11.526-9.803h11.298zm108.463 17.845 8.462-47.986h13.385l-8.462 47.986zm1.448-47.433-2.016 11.419c-6.341-1.97-12.883-1.343-14.893-.942L120.806 72h-13.394l8.032-45.52c4.357-1.803 15.055-5.462 28.789-1.925m-54.27-1.232h-10.42c-10.152 0-19.83 8.221-21.619 18.37L55.819 53.62c-1.79 10.15 4.981 18.37 15.135 18.37h22.163l2.273-12.895H74.562c-3.38 0-5.644-2.737-5.041-6.127l.07-.405h33.58l1.918-10.87c1.789-10.149-4.982-18.37-15.135-18.37zm1.69 17.342-.079.692H71.606l.109-.613c.602-3.38 3.687-6.354 7.077-6.354h7.8c3.351 0 5.615 2.925 5.061 6.275m111.81-16.663h13.394c-4.563 12.126-15.019 32.755-25.149 47.986h-13.394c-4.65-14.496-7.653-34.695-8.225-47.986h13.395c.241 4.335 2.183 20.417 4.399 31.651 6.077-10.93 12.777-25.269 15.569-31.651z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
6
auth/assets/custom-icons/icons/htx.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#fff" d="M301.399 153.471c1.48-73.53-40.157-137.614-61.84-153.005-.098-.1-2.022-1.16-1.874 1.726 0 .05-.05.05-.05.099-2.219 138.897-73.628 176.315-112.38 227.227-86.16 113.194-14.281 242.323 78.86 266.792 1.578.395 5.895 1.677 14.06 3.355 4.266.912 5.5-2.714 2.367-8.116-11.124-19.363-30.932-52.046-34.854-94.028-8.904-97.482 113.984-158.506 115.71-244.05z"/>
|
||||
<path fill="#008CD6" d="M348.339 200.56c-.74-.543-1.776-.494-1.875.444-1.973 17.66-20.25 54.315-44.325 88.577-81.005 115.513-40.207 167.904-8.51 204.262 5.846 6.734 8.756 5.23 11.84.444 2.86-4.563 7.227-10.36 25.949-19.215 2.91-1.381 73.604-38.923 81.276-124.393 7.4-82.681-45.633-134.974-64.355-150.12z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 790 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" viewBox="0 0 512 142.04"><path d="M0 34.607h30.475l17.69 45.324 17.95-45.324h29.7l-44.68 107.436H21.306l12.268-28.409zm126.676-1.808c-22.856 0-37.318 20.532-37.318 40.934 0 22.985 15.883 41.193 36.931 41.193 15.754 0 21.694-9.556 21.694-9.556v7.49h26.6V34.607h-26.6v7.102c-.13 0-6.715-8.91-21.307-8.91m5.682 25.18c10.589 0 16.012 8.394 16.012 15.883 0 8.135-5.81 16.142-16.012 16.142-8.393 0-16.012-6.844-16.012-15.754 0-9.04 6.07-16.27 16.012-16.27m51.265 54.88V0h27.763v41.967s6.585-9.168 20.402-9.168c16.916 0 26.86 12.655 26.86 30.604v49.457h-27.635V70.118c0-6.07-2.84-12.01-9.426-12.01-6.715 0-10.201 5.94-10.201 12.01v42.742zM306.038 32.8c-26.214 0-41.838 19.886-41.838 41.322 0 24.276 18.853 40.934 41.967 40.934 22.34 0 41.838-15.883 41.838-40.547 0-26.988-20.532-41.709-41.967-41.709m.258 25.31c9.297 0 15.625 7.747 15.625 15.882 0 6.973-5.94 15.625-15.625 15.625-8.91 0-15.495-7.102-15.495-15.754 0-8.135 5.423-15.754 15.495-15.754m87.938-25.31c-26.214 0-41.839 19.886-41.839 41.322 0 24.276 18.853 40.934 41.968 40.934 22.34 0 41.838-15.883 41.838-40.547 0-26.988-20.403-41.709-41.967-41.709m.258 25.31c9.297 0 15.625 7.747 15.625 15.882 0 6.973-5.94 15.625-15.625 15.625-8.91 0-15.496-7.102-15.496-15.754 0-8.135 5.553-15.754 15.496-15.754m63.66 19.498c10.202 0 18.466 8.264 18.466 18.466 0 10.2-8.264 18.465-18.465 18.465s-18.466-8.264-18.466-18.465 8.265-18.466 18.466-18.466m24.536-6.715H449.5L478.943 0H512z" fill="#5F01D1"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><circle cx="512" cy="512" r="512" style="fill:#6001d2"/><path d="M256 390.61h101.45l59.06 151 59.83-151H575L426.38 748.15H327l40.66-94.74Zm332.7 143.54a61.36 61.36 0 1 0 61.37 61.35 61.36 61.36 0 0 0-61.37-61.35M768 275.85H657.83l-98.09 235.67h110.55Z" style="fill:#fff"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 344 B |
1
auth/lib/l10n/arb/app_ca.arb
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -9,7 +9,7 @@
|
||||
"onBoardingBody": "Sichern Sie Ihre 2FA-Codes",
|
||||
"onBoardingGetStarted": "Los geht's",
|
||||
"setupFirstAccount": "Ihr erstes Konto einrichten",
|
||||
"importScanQrCode": "Scannen eines QR-Codes",
|
||||
"importScanQrCode": "QR-Code scannen",
|
||||
"qrCode": "QR-Code",
|
||||
"importEnterSetupKey": "Einen Setup-Schlüssel eingeben",
|
||||
"importAccountPageTitle": "Kontodaten eingeben",
|
||||
@@ -31,7 +31,7 @@
|
||||
"timeBasedKeyType": "Zeitbasiert (TOTP)",
|
||||
"counterBasedKeyType": "Zählerbasiert (HOTP)",
|
||||
"saveAction": "Speichern",
|
||||
"nextTotpTitle": "Weiter",
|
||||
"nextTotpTitle": "Nächster Code",
|
||||
"deleteCodeTitle": "Code löschen?",
|
||||
"deleteCodeMessage": "Sind Sie sicher, dass Sie diesen Code löschen wollen? Diese Aktion ist unumkehrbar.",
|
||||
"viewLogsAction": "Protokolle anzeigen",
|
||||
@@ -139,7 +139,7 @@
|
||||
"inFamilyPlanMessage": "Sie haben einen Familien-Plan!",
|
||||
"swipeHint": "Wischen Sie nach links, um Codes zu bearbeiten oder zu entfernen",
|
||||
"scan": "Scannen",
|
||||
"scanACode": "Scannen eines Codes",
|
||||
"scanACode": "Scan einen Code",
|
||||
"verify": "Überprüfen Sie",
|
||||
"verifyEmail": "E-Mail-Adresse verifizieren",
|
||||
"enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.",
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"codeIssuerHint": "Emisor",
|
||||
"codeSecretKeyHint": "Llave Secreta",
|
||||
"codeAccountHint": "Cuenta (tu@dominio.com)",
|
||||
"codeTagHint": "Marcado",
|
||||
"accountKeyType": "Tipo de llave",
|
||||
"sessionExpired": "La sesión ha expirado",
|
||||
"@sessionExpired": {
|
||||
"description": "Title of the dialog when the users current session is invalid/expired"
|
||||
@@ -77,12 +79,14 @@
|
||||
"data": "Datos",
|
||||
"importCodes": "Importar códigos",
|
||||
"importTypePlainText": "Texto sin formato",
|
||||
"importTypeEnteEncrypted": "Exportación cifrada Ente",
|
||||
"passwordForDecryptingExport": "Contraseña para descifrar exportación",
|
||||
"passwordEmptyError": "La contraseña no puede estar vacía",
|
||||
"importFromApp": "Importar códigos de {appName}",
|
||||
"importGoogleAuthGuide": "Exportar tus cuentas desde Google Authenticator a un código QR usando la opción \"Transferir Cuentas\". A continuación, usando otro dispositivo, escanee el código QR.\n\nConsejo: Puede usar la webcam de su portátil para tomar una foto del código QR.",
|
||||
"importSelectJsonFile": "Seleccione el archivo JSON",
|
||||
"importSelectAppExport": "Seleccione el archivo de exportación de {appName}",
|
||||
"importEnteEncGuide": "Seleccione el archivo JSON cifrado exportado desde Ente",
|
||||
"importRaivoGuide": "Utilice la opción \"Exportar códigos a un archivo de Zip\" en la configuración de Raivo.\n\nExtraiga el archivo zip e importe el archivo JSON.",
|
||||
"importBitwardenGuide": "Use la opción \"Exportar caja fuerte\" dentro del menú Herramientas de Bitwarden e importe el fichero JSON no crifrado.",
|
||||
"importAegisGuide": "Utilice la opción \"Exportar la bóveda\" en ajustes de Aegis.\n\nSi tu bóveda es cifrada, necesitara entrar contraseña de bóveda para descifrar la bóveda.",
|
||||
@@ -119,12 +123,15 @@
|
||||
"oops": "Ups",
|
||||
"suggestFeatures": "Sugerir funcionalidades",
|
||||
"faq": "Preguntas Frecuentes",
|
||||
"faq_q_1": "¿Qué tan seguro es Auth?",
|
||||
"faq_a_1": "Todos los códigos que copia de seguridad vía Ente se almacenan cifrados de extremo a extremo. Esto significa que solo usted puede acceder a sus códigos. Nuestras aplicaciones son de código abierto y nuestra criptografía ha sido auditada externamente.",
|
||||
"faq_q_2": "¿Puedo acceder a mis códigos en el escritorio?",
|
||||
"faq_a_2": "Puede acceder a tus códigos en la web en auth.ente.io.",
|
||||
"faq_q_3": "¿Cómo puedo borrar códigos?",
|
||||
"faq_a_3": "Puede eliminar un código deslizando a la izquierda en ese elemento.",
|
||||
"faq_q_4": "¿Cómo puedo apoyar este proyecto?",
|
||||
"faq_a_4": "Puedes apoyar el desarrollo de este proyecto suscribiéndote a nuestra app de Fotos en ente.io.",
|
||||
"faq_q_5": "Cómo puedo habilitar bloqueo FaceID en Ente Auth",
|
||||
"faq_a_5": "Puede activar el bloqueo FaceID en Ajustes → Seguridad → Pantalla de bloqueo.",
|
||||
"somethingWentWrongMessage": "Algo ha ido mal, por favor, prueba otra vez",
|
||||
"leaveFamily": "Dejar plan familiar",
|
||||
@@ -151,6 +158,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"invalidQRCode": "Código QR no válido",
|
||||
"noRecoveryKeyTitle": "¿Sin clave de recuperación?",
|
||||
"enterEmailHint": "Introduce tu dirección de correo electrónico",
|
||||
"invalidEmailTitle": "Dirección de correo electrónico no válida",
|
||||
@@ -195,6 +203,8 @@
|
||||
"saveKey": "Guardar Clave",
|
||||
"save": "Guardar",
|
||||
"send": "Enviar",
|
||||
"saveOrSendDescription": "¿Desea guardar esto en el almacenamiento (carpeta Descargas por defecto) o enviarlo a otras aplicaciones?",
|
||||
"saveOnlyDescription": "¿Desea guardar esto en el almacenamiento (carpeta Descargas por defecto)?",
|
||||
"back": "Atrás",
|
||||
"createAccount": "Crear cuenta",
|
||||
"passwordStrength": "Fortaleza de la contraseña: {passwordStrengthValue}",
|
||||
@@ -253,12 +263,15 @@
|
||||
"exportLogs": "Exportar registros",
|
||||
"enterYourRecoveryKey": "Introduzca su clave de recuperación",
|
||||
"tempErrorContactSupportIfPersists": "Parece que algo salió mal. Por favor, vuelve a intentarlo después de algún tiempo. Si el error persiste, ponte en contacto con nuestro equipo de soporte.",
|
||||
"networkHostLookUpErr": "No se puede conectar a Ente. Por favor, comprueba tu configuración de red y ponte en contacto con el soporte técnico si el error persiste.",
|
||||
"networkConnectionRefusedErr": "No se puede conectar a Ente. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con el soporte técnico.",
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Parece que algo salió mal. Por favor, vuelve a intentarlo después de algún tiempo. Si el error persiste, ponte en contacto con nuestro equipo de soporte.",
|
||||
"about": "Acerca de",
|
||||
"weAreOpenSource": "¡Somos de código abierto!",
|
||||
"privacy": "Privacidad",
|
||||
"terms": "Términos",
|
||||
"checkForUpdates": "Comprobar actualizaciones",
|
||||
"checkStatus": "Comprobar estado",
|
||||
"downloadUpdate": "Descargar",
|
||||
"criticalUpdateAvailable": "Actualización crítica disponible",
|
||||
"updateAvailable": "Actualizacion disponible",
|
||||
@@ -342,6 +355,7 @@
|
||||
"deleteCodeAuthMessage": "Autenticar para borrar código",
|
||||
"showQRAuthMessage": "Autenticar para mostrar código QR",
|
||||
"confirmAccountDeleteTitle": "Confirmar eliminación de la cuenta",
|
||||
"confirmAccountDeleteMessage": "Esta cuenta está vinculada a otras aplicaciones de Ente, si utilizas alguna. Se programará la eliminación de los datos cargados en todas las aplicaciones de Ente, y tu cuenta se eliminará permanentemente.",
|
||||
"androidBiometricHint": "Verificar identidad",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
@@ -402,7 +416,31 @@
|
||||
"doNotSignOut": "No cerrar la sesión",
|
||||
"hearUsWhereTitle": "¿Cómo conoció Ente? (opcional)",
|
||||
"hearUsExplanation": "No rastreamos las aplicaciones instaladas. ¡Nos ayudaría si nos dijera dónde nos encontró!",
|
||||
"recoveryKeySaved": "¡Clave de recuperación guardada en la carpeta Descargas!",
|
||||
"waitingForBrowserRequest": "Esperando la solicitud del navegador...",
|
||||
"waitingForVerification": "Esperando verificación...",
|
||||
"passkey": "Llave de acceso",
|
||||
"passKeyPendingVerification": "La verificación todavía está pendiente",
|
||||
"loginSessionExpired": "La sesión ha expirado",
|
||||
"loginSessionExpiredDetails": "Tu sesión ha expirado. Por favor, vuelve a iniciar sesión.",
|
||||
"developerSettingsWarning": "¿Estás seguro de que quieres modificar los ajustes de desarrollador?",
|
||||
"developerSettings": "Ajustes de desarrollador"
|
||||
"developerSettings": "Ajustes de desarrollador",
|
||||
"serverEndpoint": "Punto final del servidor",
|
||||
"invalidEndpoint": "Punto final no válido",
|
||||
"invalidEndpointMessage": "Lo sentimos, el punto final introducido no es válido. Por favor, introduce un punto final válido y vuelve a intentarlo.",
|
||||
"endpointUpdatedMessage": "Punto final actualizado con éxito",
|
||||
"customEndpoint": "Conectado a {endpoint}",
|
||||
"pinText": "Fijar",
|
||||
"unpinText": "Desanclar",
|
||||
"pinnedCodeMessage": "{code} ha sido anclado",
|
||||
"unpinnedCodeMessage": "{code} ha sido desanclado",
|
||||
"tags": "Etiquetas",
|
||||
"createNewTag": "Crear Nueva Etiqueta",
|
||||
"tag": "Etiqueta",
|
||||
"create": "Crear",
|
||||
"editTag": "Editar Etiqueta",
|
||||
"deleteTagTitle": "¿Eliminar etiqueta?",
|
||||
"deleteTagMessage": "¿Estás seguro de que quieres eliminar esta etiqueta? Esta acción es irreversible.",
|
||||
"somethingWentWrongParsingCode": "No se han podido analizar los códigos {x}.",
|
||||
"updateNotAvailable": "Actualización no disponible"
|
||||
}
|
||||
@@ -263,6 +263,8 @@
|
||||
"exportLogs": "Logs exporteren",
|
||||
"enterYourRecoveryKey": "Voer je herstelsleutel in",
|
||||
"tempErrorContactSupportIfPersists": "Het lijkt erop dat er iets fout is gegaan. Probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met ons supportteam.",
|
||||
"networkHostLookUpErr": "Kan geen verbinding maken met Ente, controleer uw netwerkinstellingen en neem contact op met ondersteuning als de fout zich blijft voordoen.",
|
||||
"networkConnectionRefusedErr": "Kan geen verbinding maken met Ente, probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met support.",
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Het lijkt erop dat er iets fout is gegaan. Probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met ons supportteam.",
|
||||
"about": "Over",
|
||||
"weAreOpenSource": "We zijn open source!",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"codeIssuerHint": "Wydawca",
|
||||
"codeSecretKeyHint": "Tajny klucz",
|
||||
"codeAccountHint": "Konto (ty@domena.com)",
|
||||
"codeTagHint": "Oznacz",
|
||||
"accountKeyType": "Rodzaj klucza",
|
||||
"sessionExpired": "Sesja wygasła",
|
||||
"@sessionExpired": {
|
||||
@@ -78,16 +79,19 @@
|
||||
"data": "Dane",
|
||||
"importCodes": "Importuj kody",
|
||||
"importTypePlainText": "Zwykły tekst",
|
||||
"importTypeEnteEncrypted": "Zaszyfrowany eksport ente",
|
||||
"importTypeEnteEncrypted": "Zaszyfrowany eksport Ente",
|
||||
"passwordForDecryptingExport": "Hasło do odszyfrowania eksportu",
|
||||
"passwordEmptyError": "Pole hasło nie może być puste",
|
||||
"importFromApp": "Importuj kody z {appName}",
|
||||
"importGoogleAuthGuide": "Wyeksportuj twoje konta z Google Authenticator do kodu QR używając opcji \"Przenieś konta\". Potem używając innego urządzenia, zeskanuj kod QR.",
|
||||
"importSelectJsonFile": "Wybierz plik JSON",
|
||||
"importSelectAppExport": "Wybierz plik eksportu {appName}",
|
||||
"importEnteEncGuide": "Wybierz zaszyfrowany plik JSON wyeksportowany z ente",
|
||||
"importEnteEncGuide": "Wybierz zaszyfrowany plik JSON wyeksportowany z Ente",
|
||||
"importRaivoGuide": "Użyj opcji \"Eksportuj OTP do archiwum ZIP\" w Ustawieniach Raivo.\n\nWyodrębnij plik zip i zaimportuj plik JSON.",
|
||||
"importBitwardenGuide": "Użyj opcji \"Eksportuj sejf\" w Narzędziach Bitwarden i zaimportuj niezaszyfrowany plik JSON.",
|
||||
"importAegisGuide": "Użyj opcji \"Eksportuj sejf\" w ustawieniach Aegis.\n\nJeśli twój sejf jest zaszyfrowany, musisz wprowadzić hasło sejfu, aby odszyfrować sejf.",
|
||||
"import2FasGuide": "Użyj opcji \"Ustawienia->Kopia Zapasowa-Eksport\" w 2FAS.\n\nJeśli twoja kopia zapasowa jest zaszyfrowana, musisz wprowadzić hasło, aby odszyfrować kopię zapasową",
|
||||
"importLastpassGuide": "Użyj opcji \"Przenieś konta\" w Ustawieniach Lastpass Authenticator i naciśnij \"Eksportuj konta do pliku\". Zaimportuj pobrany plik JSON.",
|
||||
"exportCodes": "Eksportuj kody",
|
||||
"importLabel": "Importuj",
|
||||
"importInstruction": "Wybierz plik, który zawiera listę twoich kodów w następującym formacie",
|
||||
@@ -100,6 +104,7 @@
|
||||
"authToChangeYourEmail": "Proszę uwierzytelnić, aby zmienić swój adres e-mail",
|
||||
"authToChangeYourPassword": "Proszę uwierzytelnić, aby zmienić hasło",
|
||||
"authToViewSecrets": "Proszę uwierzytelnić, aby wyświetlić swoje sekrety",
|
||||
"authToInitiateSignIn": "Proszę uwierzytelnić się, aby zainicjować logowanie do kopii zapasowej.",
|
||||
"ok": "Ok",
|
||||
"cancel": "Anuluj",
|
||||
"yes": "Tak",
|
||||
@@ -111,7 +116,7 @@
|
||||
"copied": "Skopiowano",
|
||||
"pleaseTryAgain": "Proszę spróbować ponownie",
|
||||
"existingUser": "Istniejący użytkownik",
|
||||
"newUser": "Nowy do Ente",
|
||||
"newUser": "Nowy/a do Ente",
|
||||
"delete": "Usuń",
|
||||
"enterYourPasswordHint": "Wprowadź swoje hasło",
|
||||
"forgotPassword": "Nie pamiętam hasła",
|
||||
@@ -119,12 +124,14 @@
|
||||
"suggestFeatures": "Zaproponuj funkcje",
|
||||
"faq": "Najczęściej zadawane pytania (FAQ)",
|
||||
"faq_q_1": "Jak bezpieczny jest Auth?",
|
||||
"faq_a_1": "Wszystkie kody, których tworzysz kopię zapasową za pomocą Ente są przechowywane zaszyfrowane end-to-end. Oznacza to, że tylko Ty możesz uzyskać dostęp do swoich kodów. Nasze aplikacje są otwarto-źródłowe, a nasza kryptografia została poddana zewnętrznemu audytowi.",
|
||||
"faq_q_2": "Czy mogę uzyskać dostęp do moich kodów na komputerze?",
|
||||
"faq_a_2": "Możesz uzyskać dostęp do swoich kodów na stronie auth.ente.io.",
|
||||
"faq_q_3": "Jak mogę usunąć kody?",
|
||||
"faq_a_3": "Możesz usunąć kod, przesuwając go w lewo.",
|
||||
"faq_q_4": "Jak mogę wesprzeć ten projekt?",
|
||||
"faq_a_4": "Możesz wspierać rozwój tego projektu, subskrybując do naszej aplikacji Zdjęcia na ente.io.",
|
||||
"faq_q_5": "Jak mogę włączyć blokadę FaceID w Ente Auth",
|
||||
"faq_a_5": "Możesz włączyć blokadę FaceID w Ustawienia → Bezpieczeństwo→ Ekran blokady.",
|
||||
"somethingWentWrongMessage": "Coś poszło nie tak. Proszę, spróbuj ponownie",
|
||||
"leaveFamily": "Opuść rodzinę",
|
||||
@@ -138,6 +145,8 @@
|
||||
"enterCodeHint": "Wprowadź sześciocyfrowy kod z twojej aplikacji uwierzytelniającej",
|
||||
"lostDeviceTitle": "Zagubiono urządzenie?",
|
||||
"twoFactorAuthTitle": "Uwierzytelnianie dwuetapowe",
|
||||
"passkeyAuthTitle": "Weryfikacja kluczem dostępu",
|
||||
"verifyPasskey": "Zweryfikuj klucz dostępu",
|
||||
"recoverAccount": "Odzyskaj konto",
|
||||
"enterRecoveryKeyHint": "Wprowadź swój klucz odzyskiwania",
|
||||
"recover": "Odzyskaj",
|
||||
@@ -194,6 +203,8 @@
|
||||
"saveKey": "Zapisz klucz",
|
||||
"save": "Zapisz",
|
||||
"send": "Wyślij",
|
||||
"saveOrSendDescription": "Czy chcesz zapisać to do pamięci masowej (domyślnie folder Pobrane) czy wysłać to do innych aplikacji?",
|
||||
"saveOnlyDescription": "Czy chcesz zapisać to do pamięci masowej (domyślnie folder Pobrane)?",
|
||||
"back": "Wstecz",
|
||||
"createAccount": "Utwórz konto",
|
||||
"passwordStrength": "Siła hasła: {passwordStrengthValue}",
|
||||
@@ -252,12 +263,15 @@
|
||||
"exportLogs": "Eksportuj logi",
|
||||
"enterYourRecoveryKey": "Wprowadź swój klucz odzyskiwania",
|
||||
"tempErrorContactSupportIfPersists": "Wygląda na to, że coś poszło nie tak. Spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z naszym zespołem pomocy technicznej.",
|
||||
"networkHostLookUpErr": "Nie można połączyć się z Ente, sprawdź ustawienia sieci i skontaktuj się z pomocą techniczną, jeśli błąd będzie się powtarzał.",
|
||||
"networkConnectionRefusedErr": "Nie można połączyć się z Ente, spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z pomocą techniczną.",
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Wygląda na to, że coś poszło nie tak. Spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z naszym zespołem pomocy technicznej.",
|
||||
"about": "O nas",
|
||||
"weAreOpenSource": "Posiadamy otwarte źródło!",
|
||||
"privacy": "Prywatność",
|
||||
"terms": "Warunki",
|
||||
"checkForUpdates": "Sprawdź czy są dostępne nowe aktualizacje",
|
||||
"checkStatus": "Sprawdź stan",
|
||||
"downloadUpdate": "Pobierz",
|
||||
"criticalUpdateAvailable": "Dostępna jest krytyczna aktualizacja",
|
||||
"updateAvailable": "Dostępna jest aktualizacja",
|
||||
@@ -333,6 +347,7 @@
|
||||
"offlineModeWarning": "Wybrałeś kontynuację bez kopii zapasowych. Proszę wykonywać ręczne kopie zapasowe, aby upewnić się, że Twoje kody są bezpieczne.",
|
||||
"showLargeIcons": "Pokaż duże ikony",
|
||||
"shouldHideCode": "Ukryj kody",
|
||||
"doubleTapToViewHiddenCode": "Możesz kliknąć dwukrotnie na wpis, aby wyświetlić kod",
|
||||
"focusOnSearchBar": "Uaktywnij wyszukiwanie przy uruchamianiu aplikacji",
|
||||
"confirmUpdatingkey": "Czy na pewno chcesz zaktualizować tajny klucz?",
|
||||
"minimizeAppOnCopy": "Minimalizuj aplikację przy kopiowaniu",
|
||||
@@ -340,6 +355,7 @@
|
||||
"deleteCodeAuthMessage": "Uwierzytelnij, aby usunąć kod",
|
||||
"showQRAuthMessage": "Uwierzytelnij, aby pokazać kod QR",
|
||||
"confirmAccountDeleteTitle": "Potwierdź usunięcie konta",
|
||||
"confirmAccountDeleteMessage": "To konto jest połączone z innymi aplikacjami Ente, jeśli ich używasz.\n\nTwoje przesłane dane, we wszystkich aplikacjach Ente, zostaną zaplanowane do usunięcia, a Twoje konto zostanie trwale usunięte.",
|
||||
"androidBiometricHint": "Potwierdź swoją tożsamość",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
@@ -364,10 +380,30 @@
|
||||
"@androidBiometricRequiredTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidDeviceCredentialsRequiredTitle": "Wymagane dane logowania urządzenia",
|
||||
"@androidDeviceCredentialsRequiredTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidDeviceCredentialsSetupDescription": "Wymagane dane logowania urządzenia",
|
||||
"@androidDeviceCredentialsSetupDescription": {
|
||||
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
|
||||
},
|
||||
"goToSettings": "Przejdź do Ustawień",
|
||||
"@goToSettings": {
|
||||
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
|
||||
},
|
||||
"androidGoToSettingsDescription": "Uwierzytelnianie biometryczne nie jest skonfigurowane na tym urządzeniu. Przejdź do 'Ustawienia > Bezpieczeństwo', aby dodać uwierzytelnianie biometryczne.",
|
||||
"@androidGoToSettingsDescription": {
|
||||
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
|
||||
},
|
||||
"iOSLockOut": "Uwierzytelnianie biometryczne jest wyłączone. Proszę zablokować i odblokować ekran, aby je włączyć.",
|
||||
"@iOSLockOut": {
|
||||
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
|
||||
},
|
||||
"iOSGoToSettingsDescription": "Uwierzytelnianie biometryczne nie jest skonfigurowane na Twoim urządzeniu. Proszę włączyć Touch ID lub Face ID na swoim telefonie.",
|
||||
"@iOSGoToSettingsDescription": {
|
||||
"description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side."
|
||||
},
|
||||
"iOSOkButton": "OK",
|
||||
"@iOSOkButton": {
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||
@@ -379,19 +415,32 @@
|
||||
"signOutOtherDevices": "Wyloguj z pozostałych urządzeń",
|
||||
"doNotSignOut": "Nie wylogowuj mnie",
|
||||
"hearUsWhereTitle": "Jak usłyszałeś o Ente? (opcjonalnie)",
|
||||
"hearUsExplanation": "Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!",
|
||||
"recoveryKeySaved": "Klucz odzyskiwania zapisany w folderze Pobrane!",
|
||||
"waitingForBrowserRequest": "Oczekiwanie na żądanie przeglądarki...",
|
||||
"waitingForVerification": "Oczekiwanie na weryfikację...",
|
||||
"passkey": "Klucz dostępu",
|
||||
"passKeyPendingVerification": "Weryfikacja jest nadal w toku",
|
||||
"loginSessionExpired": "Sesja wygasła",
|
||||
"loginSessionExpiredDetails": "Twoja sesja wygasła. Zaloguj się ponownie.",
|
||||
"developerSettingsWarning": "Czy na pewno chcesz zmodyfikować ustawienia programisty?",
|
||||
"developerSettings": "Ustawienia deweloperskie",
|
||||
"serverEndpoint": "Punkt końcowy serwera",
|
||||
"invalidEndpoint": "Punkt końcowy jest nieprawidłowy",
|
||||
"invalidEndpointMessage": "Niestety, wprowadzony punkt końcowy jest nieprawidłowy. Wprowadź prawidłowy punkt końcowy i spróbuj ponownie.",
|
||||
"endpointUpdatedMessage": "Punkt końcowy zaktualizowany pomyślnie",
|
||||
"customEndpoint": "Połączono z {endpoint}",
|
||||
"pinText": "Przypnij",
|
||||
"unpinText": "Odepnij",
|
||||
"pinnedCodeMessage": "Przypięto {code}",
|
||||
"unpinnedCodeMessage": "Odpięto {code}",
|
||||
"tags": "Etykiety",
|
||||
"createNewTag": "Utwórz Nową Etykietę",
|
||||
"tag": "Etykieta",
|
||||
"create": "Utwórz",
|
||||
"editTag": "Edytuj Etykietę",
|
||||
"deleteTagTitle": "Usunąć etykietę?",
|
||||
"deleteTagMessage": "Czy na pewno chcesz usunąć tę etykietę? Ta akcja jest nieodwracalna.",
|
||||
"somethingWentWrongParsingCode": "Nie udało się przetworzyć kodów {x}.",
|
||||
"updateNotAvailable": "Aktualizacja jest niedostępna"
|
||||
}
|
||||
@@ -146,7 +146,7 @@
|
||||
"lostDeviceTitle": "Perdeu um dispositivo?",
|
||||
"twoFactorAuthTitle": "Autenticação de dois fatores",
|
||||
"passkeyAuthTitle": "Autenticação via Chave de acesso",
|
||||
"verifyPasskey": "Verificar senha-mestra",
|
||||
"verifyPasskey": "Verificar chave de acesso",
|
||||
"recoverAccount": "Recuperar conta",
|
||||
"enterRecoveryKeyHint": "Digite a chave de recuperação",
|
||||
"recover": "Recuperar",
|
||||
@@ -419,7 +419,7 @@
|
||||
"recoveryKeySaved": "Chave de recuperação salva na pasta Downloads!",
|
||||
"waitingForBrowserRequest": "Aguardando solicitação do navegador...",
|
||||
"waitingForVerification": "Esperando por verificação...",
|
||||
"passkey": "Senha-mestra",
|
||||
"passkey": "Chave de acesso",
|
||||
"passKeyPendingVerification": "A verificação ainda está pendente",
|
||||
"loginSessionExpired": "Sessão expirada",
|
||||
"loginSessionExpiredDetails": "Sua sessão expirou. Por favor, entre novamente.",
|
||||
@@ -442,5 +442,5 @@
|
||||
"deleteTagTitle": "Apagar etiqueta?",
|
||||
"deleteTagMessage": "Tem certeza de que deseja excluir esta etiqueta? Essa ação é irreversível.",
|
||||
"somethingWentWrongParsingCode": "Não foi possível analisar os códigos {x}.",
|
||||
"updateNotAvailable": "Atualização não está disponível"
|
||||
"updateNotAvailable": "Atualização indisponível"
|
||||
}
|
||||
232
auth/lib/l10n/arb/app_ro.arb
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"account": "Cont",
|
||||
"unlock": "Deblochează",
|
||||
"recoveryKey": "Cheie de recuperare",
|
||||
"onBoardingBody": "Salvează în siguranță codurile 2FA",
|
||||
"onBoardingGetStarted": "Începe",
|
||||
"setupFirstAccount": "Configurează primul cont",
|
||||
"importScanQrCode": "Scanează un cod QR",
|
||||
"qrCode": "Cod QR",
|
||||
"importEnterSetupKey": "Introdu o cheie de configurare",
|
||||
"importAccountPageTitle": "Introdu detaliile contului",
|
||||
"secretCanNotBeEmpty": "Secretul nu poate fi gol",
|
||||
"incorrectDetails": "Detalii incorecte",
|
||||
"pleaseVerifyDetails": "Te rog verifică detaliile și încearcă din nou",
|
||||
"codeIssuerHint": "Emitent",
|
||||
"codeSecretKeyHint": "Cheie Secretă",
|
||||
"codeAccountHint": "Cont (tu@domeniu.com)",
|
||||
"codeTagHint": "Etichetă",
|
||||
"accountKeyType": "Tipul de cheie",
|
||||
"sessionExpired": "Sesiune expirată",
|
||||
"@sessionExpired": {
|
||||
"description": "Title of the dialog when the users current session is invalid/expired"
|
||||
},
|
||||
"pleaseLoginAgain": "Te rugăm să te autentifici din nou",
|
||||
"loggingOut": "Deconectare...",
|
||||
"timeBasedKeyType": "Bazat pe timp (TOTP)",
|
||||
"counterBasedKeyType": "Bazat pe contor (HOTP)",
|
||||
"saveAction": "Salvare",
|
||||
"nextTotpTitle": "următor",
|
||||
"deleteCodeTitle": "Șterge codul?",
|
||||
"deleteCodeMessage": "Ești sigur că vrei să ștergi acest cod? Acțiunea este ireversibilă.",
|
||||
"viewLogsAction": "Afișare jurnale",
|
||||
"preparingLogsTitle": "Se pregătesc jurnalele...",
|
||||
"emailLogsTitle": "Jurnale e-mail",
|
||||
"emailLogsMessage": "Te rugăm să trimiți jurnalele la {email}",
|
||||
"@emailLogsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyEmailAction": "Copiază e-mailul",
|
||||
"exportLogsAction": "Exportează log-urile",
|
||||
"reportABug": "Raportează o eroare",
|
||||
"crashAndErrorReporting": "Închidere accidentală şi raportare erori",
|
||||
"reportBug": "Raportare bug",
|
||||
"emailUsMessage": "Te rugăm să ne trimiți un e-mail la {email}",
|
||||
"@emailUsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contactSupport": "Contactează suportul",
|
||||
"rateUsOnStore": "Evaluează-ne pe {storeName}",
|
||||
"blog": "Blog",
|
||||
"verifyPassword": "Verifică parola",
|
||||
"pleaseWait": "Te rog așteaptă...",
|
||||
"recreatePassword": "Recreează parola",
|
||||
"incorrectPasswordTitle": "Parolă incorectă",
|
||||
"welcomeBack": "Bine ai revenit!",
|
||||
"supportDevs": "Abonează-te la <bold-green>ente</bold-green> pentru a ne susține",
|
||||
"supportDiscount": "Folosește codul \"AUTH\" pentru a obține o reducere de 10% în primul an",
|
||||
"changeEmail": "Schimbă e-mailul",
|
||||
"changePassword": "Schimbă parola",
|
||||
"data": "Date",
|
||||
"importCodes": "Importă coduri",
|
||||
"importTypePlainText": "Text simplu",
|
||||
"passwordForDecryptingExport": "Parola pentru a decripta exportul",
|
||||
"passwordEmptyError": "Parola nu poate fi goală",
|
||||
"importFromApp": "Importă coduri din {appName}",
|
||||
"importSelectJsonFile": "Selectează fișierul JSON",
|
||||
"importSelectAppExport": "Selectează fișierul de export din {appName}",
|
||||
"importEnteEncGuide": "Selectează fișierul criptat JSON exportat din Bențe",
|
||||
"exportCodes": "Exportă coduri",
|
||||
"importLabel": "Importă",
|
||||
"selectFile": "Selectează fișier",
|
||||
"emailVerificationToggle": "Verificarea adresei de e-mail",
|
||||
"authToChangeEmailVerificationSetting": "Te rugăm să te autentifici pentru a schimba verificarea prin e-mail",
|
||||
"authToChangeYourEmail": "Te rugăm să te autentifici pentru a-ți schimba adresa de e-mail",
|
||||
"ok": "Ok",
|
||||
"cancel": "Anulare",
|
||||
"yes": "Da",
|
||||
"no": "Nu",
|
||||
"email": "E-mail",
|
||||
"support": "Asistență",
|
||||
"general": "General",
|
||||
"settings": "Setări",
|
||||
"copied": "S-a copiat",
|
||||
"pleaseTryAgain": "Te rugăm să încerci din nou",
|
||||
"existingUser": "Utilizator existent",
|
||||
"newUser": "Nou în Ente",
|
||||
"delete": "Ștergere",
|
||||
"enterYourPasswordHint": "Introdu parola",
|
||||
"forgotPassword": "Am uitat parola",
|
||||
"oops": "Ups",
|
||||
"suggestFeatures": "Sugerează funcționalități",
|
||||
"faq": "Întrebări frecvente",
|
||||
"faq_q_1": "Cât de sigur este Auth?",
|
||||
"faq_q_2": "Pot accesa codurile de pe desktop?",
|
||||
"faq_q_3": "Cum pot șterge codurile?",
|
||||
"faq_q_4": "Cum pot susţine acest proiect?",
|
||||
"faq_a_4": "Poți susține dezvoltarea acestui proiect prin cumpărarea unui abonament la aplicația noastră de fotografii @ ente.io.",
|
||||
"faq_q_5": "Cum pot activa blocarea cu FaceID în Auth",
|
||||
"faq_a_5": "Poți activa blocarea FaceID din Setări → Securitate → Ecran de blocare.",
|
||||
"somethingWentWrongMessage": "Ceva n-a mers bine, te rog încearcă din nou",
|
||||
"leaveFamily": "Părăsește familia",
|
||||
"verifyEmail": "Verifică e-mail",
|
||||
"twoFactorAuthTitle": "Autentificare cu doi factori",
|
||||
"recoverAccount": "Recuperare cont",
|
||||
"recover": "Recuperează",
|
||||
"invalidQRCode": "Codul QR nu este valid",
|
||||
"enterEmailHint": "Introdu adresa ta de e-mail",
|
||||
"invalidEmailTitle": "Adresa e-mail nu este validă",
|
||||
"invalidEmailMessage": "Te rugăm să introduci o adresă de e-mail validă.",
|
||||
"deleteAccount": "Ștergere cont",
|
||||
"yesSendFeedbackAction": "Da, trimite feedback",
|
||||
"noDeleteAccountAction": "Nu, șterge contul",
|
||||
"initiateAccountDeleteTitle": "Te rugăm să te autentifici pentru a iniția ștergerea contului",
|
||||
"sendEmail": "Trimite e-mail",
|
||||
"confirmPassword": "Confirmă parola",
|
||||
"close": "Închide",
|
||||
"selectLanguage": "Selectare limbă",
|
||||
"language": "Limbă",
|
||||
"social": "Social",
|
||||
"security": "Securitate",
|
||||
"lockscreen": "Ecran de blocare",
|
||||
"scanAQrCode": "Scanează un cod QR",
|
||||
"copiedToClipboard": "Copiat în clipboard",
|
||||
"copiedNextToClipboard": "Codul următor a fost copiat în clipboard",
|
||||
"error": "Eroare",
|
||||
"recoveryKeyOnForgotPassword": "Dacă îți uiți parola, singura modalitate prin care poți recupera datele este cu această cheie.",
|
||||
"saveKey": "Salvare cheie",
|
||||
"save": "Salvare",
|
||||
"send": "Trimitere",
|
||||
"back": "Înapoi",
|
||||
"password": "Parolă",
|
||||
"termsOfServicesTitle": "Termeni",
|
||||
"setPasswordTitle": "Setează parola",
|
||||
"changePasswordTitle": "Schimbă parola",
|
||||
"resetPasswordTitle": "Resetează parola",
|
||||
"passwordWarning": "Nu stocăm această parolă, deci dacă o uiți, <underline>nu îți putem decripta datele</underline>",
|
||||
"enterPasswordToEncrypt": "Introdu o parolă pe care o putem folosi pentru a-ți cripta datele",
|
||||
"passwordChangedSuccessfully": "Parola a fost modificată cu succes",
|
||||
"recreatePasswordTitle": "Recreează parola",
|
||||
"invalidKey": "Cheie invalidă",
|
||||
"tryAgain": "Încearcă din nou",
|
||||
"viewRecoveryKey": "Vezi cheia de recuperare",
|
||||
"pleaseSendTheLogsTo": "Te rugăm să trimiți jurnalele la {toEmail}",
|
||||
"copyEmailAddress": "Copiază adresa de e-mail",
|
||||
"about": "Despre",
|
||||
"weAreOpenSource": "Suntem open source!",
|
||||
"privacy": "Confidențialitate",
|
||||
"terms": "Termeni",
|
||||
"checkForUpdates": "Verifică actualizări",
|
||||
"checkStatus": "Verifică status",
|
||||
"downloadUpdate": "Descărcare",
|
||||
"criticalUpdateAvailable": "Actualizare critică disponibilă",
|
||||
"updateAvailable": "Actualizare disponibilă",
|
||||
"update": "Actualizare",
|
||||
"checking": "Se verifică...",
|
||||
"youAreOnTheLatestVersion": "Utilizezi cea mai recentă versiune",
|
||||
"warning": "Atenție",
|
||||
"iUnderStand": "Înţeleg",
|
||||
"@iUnderStand": {
|
||||
"description": "Text for the button to confirm the user understands the warning"
|
||||
},
|
||||
"importSuccessTitle": "Ura!",
|
||||
"sorry": "Ne pare rău",
|
||||
"pendingSyncs": "Atenție",
|
||||
"tapToEnterCode": "Apasă pentru a introduce codul",
|
||||
"resendEmail": "Retrimite e-mail",
|
||||
"weHaveSendEmailTo": "Am trimis un e-mail la <green>{email}</green>",
|
||||
"@weHaveSendEmailTo": {
|
||||
"description": "Text to indicate that we have sent a mail to the user",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"description": "The email address of the user",
|
||||
"type": "String",
|
||||
"example": "example@ente.io"
|
||||
}
|
||||
}
|
||||
},
|
||||
"activeSessions": "Sesiuni active",
|
||||
"somethingWentWrongPleaseTryAgain": "Ceva n-a mers bine, te rog încearcă din nou",
|
||||
"thisDevice": "Acest dispozitiv",
|
||||
"toResetVerifyEmail": "Pentru a reseta parola, te rugăm să confirmi mai întâi adresa de e-mail.",
|
||||
"thisEmailIsAlreadyInUse": "Această adresă de e-mail este deja folosită",
|
||||
"emailChangedTo": "E-mail modificat în {newEmail}",
|
||||
"enterPassword": "Introdu parola",
|
||||
"passwordToEncryptExport": "Parolă pentru a cripta exportul",
|
||||
"useOffline": "Utilizează fără backup-uri",
|
||||
"offlineModeWarning": "Ai ales să continui fără backup-uri. Te rog salvează-ți backup-urile manual ca să fii sigur că ai codurile în siguranță.",
|
||||
"showLargeIcons": "Afișează iconițele mari",
|
||||
"shouldHideCode": "Ascunde codurile",
|
||||
"androidBiometricHint": "Verifică identitatea",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidBiometricNotRecognized": "Neidentificat. Încearcă din nou.",
|
||||
"@androidBiometricNotRecognized": {
|
||||
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidBiometricSuccess": "Succes",
|
||||
"@androidBiometricSuccess": {
|
||||
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidCancelButton": "Anulare",
|
||||
"@androidCancelButton": {
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
|
||||
},
|
||||
"androidSignInTitle": "Autentificare necesară",
|
||||
"@androidSignInTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"goToSettings": "Mergi la setări",
|
||||
"@goToSettings": {
|
||||
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
|
||||
},
|
||||
"iOSOkButton": "Ok",
|
||||
"@iOSOkButton": {
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||
},
|
||||
"noInternetConnection": "Nu există conexiune la internet",
|
||||
"signOutFromOtherDevices": "Deconectare de pe alte dispozitive",
|
||||
"signOutOtherDevices": "Deconectează alte dispozitive",
|
||||
"doNotSignOut": "Nu te deconecta",
|
||||
"loginSessionExpired": "Sesiune expirată",
|
||||
"updateNotAvailable": "Actualizare indisponibilă"
|
||||
}
|
||||
@@ -269,6 +269,7 @@
|
||||
"privacy": "Gizlilik",
|
||||
"terms": "Şartlar",
|
||||
"checkForUpdates": "Güncellemeleri denetleyin",
|
||||
"checkStatus": "Durumu kontrol et",
|
||||
"downloadUpdate": "İndir",
|
||||
"criticalUpdateAvailable": "Kritik güncelleme mevcut",
|
||||
"updateAvailable": "Güncelleme mevcut",
|
||||
@@ -417,6 +418,9 @@
|
||||
"waitingForBrowserRequest": "Tarayıcı isteği bekleniyor...",
|
||||
"waitingForVerification": "Doğrulama bekleniyor...",
|
||||
"passkey": "Geçiş anahtarı",
|
||||
"passKeyPendingVerification": "Doğrulama hala bekliyor",
|
||||
"loginSessionExpired": "Oturum süresi doldu",
|
||||
"loginSessionExpiredDetails": "Oturum süreniz doldu. Tekrar giriş yapın.",
|
||||
"developerSettingsWarning": "Geliştirici ayarlarını değiştirmekten emin misiniz?",
|
||||
"developerSettings": "Geliştirici ayarları",
|
||||
"serverEndpoint": "Sunucu uç noktası",
|
||||
|
||||
@@ -112,21 +112,14 @@ resources (`build`) folder. This is used for thumbnail generation on Linux.
|
||||
On macOS, we use the `sips` CLI tool for conversion, but that is already
|
||||
available on the host machine, and is not bundled with our app.
|
||||
|
||||
### AI/ML
|
||||
### ML
|
||||
|
||||
[onnxruntime-node](https://github.com/Microsoft/onnxruntime) is used as the
|
||||
AI/ML runtime. It powers both natural language searches (using CLIP) and face
|
||||
[onnxruntime-node](https://github.com/Microsoft/onnxruntime) is used as the ML
|
||||
runtime. It powers both natural language searches (using CLIP) and face
|
||||
detection (using YOLO).
|
||||
|
||||
[jpeg-js](https://github.com/jpeg-js/jpeg-js#readme) is used for decoding JPEG
|
||||
data into raw RGB bytes before passing it to ONNX.
|
||||
|
||||
html-entities is used by the bundled clip-bpe-ts tokenizer for CLIP.
|
||||
|
||||
### Watch Folders
|
||||
|
||||
[chokidar](https://github.com/paulmillr/chokidar) is used as a file system
|
||||
watcher for the watch folders functionality.
|
||||
[clip-bpe-js](https://github.com/simonwarchol/clip-bpe-js) is used for tokening
|
||||
the user's search phrase before computing its CLIP (text) embedding.
|
||||
|
||||
### ZIP
|
||||
|
||||
@@ -135,3 +128,8 @@ reading of large ZIP files (e.g. during imports of Google Takeout ZIPs).
|
||||
|
||||
[lru-cache](https://github.com/isaacs/node-lru-cache) is used to cache file ZIP
|
||||
handles to avoid reopening them for every operation.
|
||||
|
||||
### Watch folders
|
||||
|
||||
[chokidar](https://github.com/paulmillr/chokidar) is used as a file system
|
||||
watcher for the watch folders functionality.
|
||||
|
||||
@@ -38,6 +38,13 @@ export default ts.config(
|
||||
ignoreArrowShorthand: true,
|
||||
},
|
||||
],
|
||||
// Allow free standing ternary expressions.
|
||||
"@typescript-eslint/no-unused-expressions": [
|
||||
"error",
|
||||
{
|
||||
allowTernary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -33,15 +33,13 @@
|
||||
"electron-store": "^8.2",
|
||||
"electron-updater": "^6.2",
|
||||
"ffmpeg-static": "^5.2",
|
||||
"html-entities": "^2.5",
|
||||
"jpeg-js": "^0.4",
|
||||
"lru-cache": "^10.2",
|
||||
"next-electron-server": "^1",
|
||||
"node-stream-zip": "^1.15",
|
||||
"onnxruntime-node": "^1.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.4.0",
|
||||
"@eslint/js": "^9",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/auto-launch": "^5.0",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
@@ -50,13 +48,13 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^30",
|
||||
"electron-builder": "25.0.0-alpha.8",
|
||||
"eslint": "^9.4.0",
|
||||
"eslint": "^9",
|
||||
"prettier": "^3",
|
||||
"prettier-plugin-organize-imports": "^3",
|
||||
"prettier-plugin-packagejson": "^2",
|
||||
"shx": "^0.3",
|
||||
"typescript": "^5",
|
||||
"typescript-eslint": "8.0.0-alpha.10"
|
||||
"typescript-eslint": "^8.0.0-alpha.39"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"productName": "ente"
|
||||
|
||||
@@ -295,9 +295,8 @@ const createMainWindow = () => {
|
||||
// On macOS, also hide the dock icon on macOS.
|
||||
if (process.platform == "darwin") app.dock.hide();
|
||||
} else {
|
||||
// Show our window otherwise.
|
||||
//
|
||||
// If we did not give it an explicit size, maximize it
|
||||
// Show our window otherwise, maximizing it if we're not asked to set it
|
||||
// to a specific size.
|
||||
bounds ? window.show() : window.maximize();
|
||||
}
|
||||
|
||||
@@ -538,7 +537,7 @@ const setupTrayItem = (mainWindow: BrowserWindow) => {
|
||||
* old cache dir if it exists.
|
||||
*
|
||||
* Added May 2024, v1.7.0. This migration code can be removed after some time
|
||||
* once most people have upgraded to newer versions.
|
||||
* once most people have upgraded to newer versions (tag: Migration).
|
||||
*/
|
||||
const deleteLegacyDiskCacheDirIfExists = async () => {
|
||||
const removeIfExists = async (dirPath: string) => {
|
||||
|
||||
@@ -186,10 +186,8 @@ export const attachIPCHandlers = () => {
|
||||
|
||||
// - ML
|
||||
|
||||
ipcMain.handle(
|
||||
"computeCLIPImageEmbedding",
|
||||
(_, jpegImageData: Uint8Array) =>
|
||||
computeCLIPImageEmbedding(jpegImageData),
|
||||
ipcMain.handle("computeCLIPImageEmbedding", (_, input: Float32Array) =>
|
||||
computeCLIPImageEmbedding(input),
|
||||
);
|
||||
|
||||
ipcMain.handle("computeCLIPTextEmbeddingIfAvailable", (_, text: string) =>
|
||||
|
||||
@@ -42,7 +42,7 @@ class AutoLauncher {
|
||||
if (this.autoLaunch) {
|
||||
return app.commandLine.hasSwitch("hidden");
|
||||
} else {
|
||||
return app.getLoginItemSettings().openAtLogin;
|
||||
return app.getLoginItemSettings().wasOpenedAtLogin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
// TODO: These arise from the array indexing in the pre-processing code. Isolate
|
||||
// once that code settles down to its final place (currently duplicated across
|
||||
// web and desktop).
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
|
||||
/**
|
||||
* @file Compute CLIP embeddings for images and text.
|
||||
*
|
||||
* The embeddings are computed using ONNX runtime, with CLIP as the model.
|
||||
*
|
||||
* @see `web/apps/photos/src/services/clip-service.ts` for more details.
|
||||
*/
|
||||
|
||||
import Tokenizer from "clip-bpe-js";
|
||||
import jpeg from "jpeg-js";
|
||||
import fs from "node:fs/promises";
|
||||
import * as ort from "onnxruntime-node";
|
||||
import log from "../log";
|
||||
import { writeStream } from "../stream";
|
||||
import { ensure, wait } from "../utils/common";
|
||||
import { deleteTempFile, makeTempFilePath } from "../utils/temp";
|
||||
import { makeCachedInferenceSession } from "./ml";
|
||||
|
||||
const cachedCLIPImageSession = makeCachedInferenceSession(
|
||||
@@ -25,241 +15,16 @@ const cachedCLIPImageSession = makeCachedInferenceSession(
|
||||
351468764 /* 335.2 MB */,
|
||||
);
|
||||
|
||||
export const computeCLIPImageEmbedding = async (jpegImageData: Uint8Array) => {
|
||||
const tempFilePath = await makeTempFilePath();
|
||||
const imageStream = new Response(jpegImageData.buffer).body;
|
||||
await writeStream(tempFilePath, ensure(imageStream));
|
||||
try {
|
||||
return await clipImageEmbedding_(tempFilePath);
|
||||
} finally {
|
||||
await deleteTempFile(tempFilePath);
|
||||
}
|
||||
};
|
||||
|
||||
const clipImageEmbedding_ = async (jpegFilePath: string) => {
|
||||
export const computeCLIPImageEmbedding = async (input: Float32Array) => {
|
||||
const session = await cachedCLIPImageSession();
|
||||
const t1 = Date.now();
|
||||
const rgbData = await getRGBData(jpegFilePath);
|
||||
const t = Date.now();
|
||||
const feeds = {
|
||||
input: new ort.Tensor("float32", rgbData, [1, 3, 224, 224]),
|
||||
input: new ort.Tensor("float32", input, [1, 3, 224, 224]),
|
||||
};
|
||||
const t2 = Date.now();
|
||||
const results = await session.run(feeds);
|
||||
log.debug(
|
||||
() =>
|
||||
`ONNX/CLIP image embedding took ${Date.now() - t1} ms (prep: ${t2 - t1} ms, inference: ${Date.now() - t2} ms)`,
|
||||
);
|
||||
log.debug(() => `ONNX/CLIP image embedding took ${Date.now() - t} ms`);
|
||||
/* Need these model specific casts to type the result */
|
||||
const imageEmbedding = ensure(results.output).data as Float32Array;
|
||||
return normalizeEmbedding(imageEmbedding);
|
||||
};
|
||||
|
||||
const getRGBData = async (jpegFilePath: string): Promise<Float32Array> => {
|
||||
const jpegData = await fs.readFile(jpegFilePath);
|
||||
const rawImageData = jpeg.decode(jpegData, {
|
||||
useTArray: true,
|
||||
formatAsRGBA: false,
|
||||
}); // TODO: manav: make sure this works on all images, not just jpeg
|
||||
const pixelData = rawImageData.data;
|
||||
|
||||
const requiredWidth = 224;
|
||||
const requiredHeight = 224;
|
||||
const requiredSize = 3 * requiredWidth * requiredHeight;
|
||||
const mean: number[] = [0.48145466, 0.4578275, 0.40821073];
|
||||
const std: number[] = [0.26862954, 0.26130258, 0.27577711];
|
||||
|
||||
const scale = Math.max(
|
||||
requiredWidth / rawImageData.width,
|
||||
requiredHeight / rawImageData.height,
|
||||
);
|
||||
const scaledWidth = Math.round(rawImageData.width * scale);
|
||||
const scaledHeight = Math.round(rawImageData.height * scale);
|
||||
const widthOffset = Math.max(0, scaledWidth - requiredWidth) / 2;
|
||||
const heightOffset = Math.max(0, scaledHeight - requiredHeight) / 2;
|
||||
|
||||
const processedImage = new Float32Array(requiredSize);
|
||||
|
||||
// Populate the Float32Array with normalized pixel values.
|
||||
let pi = 0;
|
||||
const cOffsetG = requiredHeight * requiredWidth; // ChannelOffsetGreen
|
||||
const cOffsetB = 2 * requiredHeight * requiredWidth; // ChannelOffsetBlue
|
||||
for (let h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) {
|
||||
for (let w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) {
|
||||
const { r, g, b } = pixelRGBBicubic(
|
||||
w / scale,
|
||||
h / scale,
|
||||
pixelData,
|
||||
rawImageData.width,
|
||||
rawImageData.height,
|
||||
);
|
||||
processedImage[pi] = (r / 255.0 - mean[0]!) / std[0]!;
|
||||
processedImage[pi + cOffsetG] = (g / 255.0 - mean[1]!) / std[1]!;
|
||||
processedImage[pi + cOffsetB] = (b / 255.0 - mean[2]!) / std[2]!;
|
||||
pi++;
|
||||
}
|
||||
}
|
||||
return processedImage;
|
||||
};
|
||||
|
||||
// NOTE: exact duplicate of the function in web/apps/photos/src/services/face/image.ts
|
||||
const pixelRGBBicubic = (
|
||||
fx: number,
|
||||
fy: number,
|
||||
imageData: Uint8Array,
|
||||
imageWidth: number,
|
||||
imageHeight: number,
|
||||
) => {
|
||||
// Clamp to image boundaries.
|
||||
fx = clamp(fx, 0, imageWidth - 1);
|
||||
fy = clamp(fy, 0, imageHeight - 1);
|
||||
|
||||
const x = Math.trunc(fx) - (fx >= 0.0 ? 0 : 1);
|
||||
const px = x - 1;
|
||||
const nx = x + 1;
|
||||
const ax = x + 2;
|
||||
const y = Math.trunc(fy) - (fy >= 0.0 ? 0 : 1);
|
||||
const py = y - 1;
|
||||
const ny = y + 1;
|
||||
const ay = y + 2;
|
||||
const dx = fx - x;
|
||||
const dy = fy - y;
|
||||
|
||||
const cubic = (
|
||||
dx: number,
|
||||
ipp: number,
|
||||
icp: number,
|
||||
inp: number,
|
||||
iap: number,
|
||||
) =>
|
||||
icp +
|
||||
0.5 *
|
||||
(dx * (-ipp + inp) +
|
||||
dx * dx * (2 * ipp - 5 * icp + 4 * inp - iap) +
|
||||
dx * dx * dx * (-ipp + 3 * icp - 3 * inp + iap));
|
||||
|
||||
const icc = pixelRGBA(imageData, imageWidth, imageHeight, x, y);
|
||||
|
||||
const ipp =
|
||||
px < 0 || py < 0
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, px, py);
|
||||
const icp =
|
||||
px < 0 ? icc : pixelRGBA(imageData, imageWidth, imageHeight, x, py);
|
||||
const inp =
|
||||
py < 0 || nx >= imageWidth
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, nx, py);
|
||||
const iap =
|
||||
ax >= imageWidth || py < 0
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, ax, py);
|
||||
|
||||
const ip0 = cubic(dx, ipp.r!, icp.r!, inp.r!, iap.r!);
|
||||
const ip1 = cubic(dx, ipp.g!, icp.g!, inp.g!, iap.g!);
|
||||
const ip2 = cubic(dx, ipp.b!, icp.b!, inp.b!, iap.b!);
|
||||
// const ip3 = cubic(dx, ipp.a, icp.a, inp.a, iap.a);
|
||||
|
||||
const ipc =
|
||||
px < 0 ? icc : pixelRGBA(imageData, imageWidth, imageHeight, px, y);
|
||||
const inc =
|
||||
nx >= imageWidth
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, nx, y);
|
||||
const iac =
|
||||
ax >= imageWidth
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, ax, y);
|
||||
|
||||
const ic0 = cubic(dx, ipc.r!, icc.r!, inc.r!, iac.r!);
|
||||
const ic1 = cubic(dx, ipc.g!, icc.g!, inc.g!, iac.g!);
|
||||
const ic2 = cubic(dx, ipc.b!, icc.b!, inc.b!, iac.b!);
|
||||
// const ic3 = cubic(dx, ipc.a, icc.a, inc.a, iac.a);
|
||||
|
||||
const ipn =
|
||||
px < 0 || ny >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, px, ny);
|
||||
const icn =
|
||||
ny >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, x, ny);
|
||||
const inn =
|
||||
nx >= imageWidth || ny >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, nx, ny);
|
||||
const ian =
|
||||
ax >= imageWidth || ny >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, ax, ny);
|
||||
|
||||
const in0 = cubic(dx, ipn.r!, icn.r!, inn.r!, ian.r!);
|
||||
const in1 = cubic(dx, ipn.g!, icn.g!, inn.g!, ian.g!);
|
||||
const in2 = cubic(dx, ipn.b!, icn.b!, inn.b!, ian.b!);
|
||||
// const in3 = cubic(dx, ipn.a, icn.a, inn.a, ian.a);
|
||||
|
||||
const ipa =
|
||||
px < 0 || ay >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, px, ay);
|
||||
const ica =
|
||||
ay >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, x, ay);
|
||||
const ina =
|
||||
nx >= imageWidth || ay >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, nx, ay);
|
||||
const iaa =
|
||||
ax >= imageWidth || ay >= imageHeight
|
||||
? icc
|
||||
: pixelRGBA(imageData, imageWidth, imageHeight, ax, ay);
|
||||
|
||||
const ia0 = cubic(dx, ipa.r!, ica.r!, ina.r!, iaa.r!);
|
||||
const ia1 = cubic(dx, ipa.g!, ica.g!, ina.g!, iaa.g!);
|
||||
const ia2 = cubic(dx, ipa.b!, ica.b!, ina.b!, iaa.b!);
|
||||
// const ia3 = cubic(dx, ipa.a, ica.a, ina.a, iaa.a);
|
||||
|
||||
const c0 = Math.trunc(clamp(cubic(dy, ip0, ic0, in0, ia0), 0, 255));
|
||||
const c1 = Math.trunc(clamp(cubic(dy, ip1, ic1, in1, ia1), 0, 255));
|
||||
const c2 = Math.trunc(clamp(cubic(dy, ip2, ic2, in2, ia2), 0, 255));
|
||||
// const c3 = cubic(dy, ip3, ic3, in3, ia3);
|
||||
|
||||
return { r: c0, g: c1, b: c2 };
|
||||
};
|
||||
|
||||
// NOTE: exact duplicate of the function in web/apps/photos/src/services/face/image.ts
|
||||
const clamp = (value: number, min: number, max: number) =>
|
||||
Math.min(max, Math.max(min, value));
|
||||
|
||||
// NOTE: exact duplicate of the function in web/apps/photos/src/services/face/image.ts
|
||||
const pixelRGBA = (
|
||||
imageData: Uint8Array,
|
||||
width: number,
|
||||
height: number,
|
||||
x: number,
|
||||
y: number,
|
||||
) => {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||
return { r: 0, g: 0, b: 0, a: 0 };
|
||||
}
|
||||
const index = (y * width + x) * 4;
|
||||
return {
|
||||
r: imageData[index],
|
||||
g: imageData[index + 1],
|
||||
b: imageData[index + 2],
|
||||
a: imageData[index + 3],
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeEmbedding = (embedding: Float32Array) => {
|
||||
let normalization = 0;
|
||||
for (const v of embedding) normalization += v * v;
|
||||
|
||||
const sqrtNormalization = Math.sqrt(normalization);
|
||||
for (let index = 0; index < embedding.length; index++)
|
||||
embedding[index] = ensure(embedding[index]) / sqrtNormalization;
|
||||
|
||||
return embedding;
|
||||
return ensure(results.output).data as Float32Array;
|
||||
};
|
||||
|
||||
const cachedCLIPTextSession = makeCachedInferenceSession(
|
||||
@@ -290,18 +55,14 @@ export const computeCLIPTextEmbeddingIfAvailable = async (text: string) => {
|
||||
}
|
||||
|
||||
const session = sessionOrSkip;
|
||||
const t1 = Date.now();
|
||||
const t = Date.now();
|
||||
const tokenizer = getTokenizer();
|
||||
const tokenizedText = Int32Array.from(tokenizer.encodeForCLIP(text));
|
||||
const feeds = {
|
||||
input: new ort.Tensor("int32", tokenizedText, [1, 77]),
|
||||
};
|
||||
const t2 = Date.now();
|
||||
|
||||
const results = await session.run(feeds);
|
||||
log.debug(
|
||||
() =>
|
||||
`ONNX/CLIP text embedding took ${Date.now() - t1} ms (prep: ${t2 - t1} ms, inference: ${Date.now() - t2} ms)`,
|
||||
);
|
||||
const textEmbedding = ensure(results.output).data as Float32Array;
|
||||
return normalizeEmbedding(textEmbedding);
|
||||
log.debug(() => `ONNX/CLIP text embedding took ${Date.now() - t} ms`);
|
||||
return ensure(results.output).data as Float32Array;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* The runtime used is ONNX.
|
||||
*/
|
||||
|
||||
import * as ort from "onnxruntime-node";
|
||||
import log from "../log";
|
||||
import { ensure } from "../utils/common";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file AI/ML related functionality, generic layer.
|
||||
* @file ML related functionality, generic layer.
|
||||
*
|
||||
* @see also `ml-clip.ts`, `ml-face.ts`.
|
||||
*
|
||||
@@ -10,6 +10,7 @@
|
||||
* can use the binary ONNX runtime which is 10-20x faster than the WASM based
|
||||
* web one.
|
||||
*/
|
||||
|
||||
import { app, net } from "electron/main";
|
||||
import { existsSync } from "fs";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
@@ -58,7 +58,7 @@ export const pendingUploads = async (): Promise<PendingUploads | undefined> => {
|
||||
const allZipItems = uploadStatusStore.get("zipItems");
|
||||
let zipItems: typeof allZipItems;
|
||||
|
||||
// Migration code - May 2024. Remove after a bit.
|
||||
// Migration code - May 2024. Remove after a bit (tag: Migration).
|
||||
//
|
||||
// The older store formats will not have zipItems and instead will have
|
||||
// zipPaths. If we find such a case, read the zipPaths and enqueue all of
|
||||
|
||||
@@ -163,8 +163,8 @@ const ffmpegExec = (
|
||||
|
||||
// - ML
|
||||
|
||||
const computeCLIPImageEmbedding = (jpegImageData: Uint8Array) =>
|
||||
ipcRenderer.invoke("computeCLIPImageEmbedding", jpegImageData);
|
||||
const computeCLIPImageEmbedding = (input: Float32Array) =>
|
||||
ipcRenderer.invoke("computeCLIPImageEmbedding", input);
|
||||
|
||||
const computeCLIPTextEmbeddingIfAvailable = (text: string) =>
|
||||
ipcRenderer.invoke("computeCLIPTextEmbeddingIfAvailable", text);
|
||||
|
||||
@@ -117,19 +117,24 @@
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
|
||||
"@eslint-community/regexpp@^4.10.0":
|
||||
version "4.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
|
||||
integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
|
||||
|
||||
"@eslint/config-array@^0.15.1":
|
||||
version "0.15.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.15.1.tgz#1fa78b422d98f4e7979f2211a1fde137e26c7d61"
|
||||
integrity sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ==
|
||||
"@eslint-community/regexpp@^4.6.1":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
||||
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
|
||||
|
||||
"@eslint/config-array@^0.17.0":
|
||||
version "0.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.0.tgz#ff305e1ee618a00e6e5d0485454c8d92d94a860d"
|
||||
integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==
|
||||
dependencies:
|
||||
"@eslint/object-schema" "^2.1.3"
|
||||
"@eslint/object-schema" "^2.1.4"
|
||||
debug "^4.3.1"
|
||||
minimatch "^3.0.5"
|
||||
minimatch "^3.1.2"
|
||||
|
||||
"@eslint/eslintrc@^3.1.0":
|
||||
version "3.1.0"
|
||||
@@ -146,15 +151,15 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@9.4.0", "@eslint/js@^9.4.0":
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.4.0.tgz#96a2edd37ec0551ce5f9540705be23951c008a0c"
|
||||
integrity sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg==
|
||||
"@eslint/js@9.6.0", "@eslint/js@^9":
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.6.0.tgz#5b0cb058cc13d9c92d4e561d3538807fa5127c95"
|
||||
integrity sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==
|
||||
|
||||
"@eslint/object-schema@^2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.3.tgz#e65ae80ee2927b4fd8c5c26b15ecacc2b2a6cc2a"
|
||||
integrity sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw==
|
||||
"@eslint/object-schema@^2.1.4":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
|
||||
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
|
||||
|
||||
"@gar/promisify@^1.1.3":
|
||||
version "1.1.3"
|
||||
@@ -323,7 +328,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
|
||||
integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.15":
|
||||
"@types/json-schema@*":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
@@ -341,9 +346,9 @@
|
||||
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
|
||||
|
||||
"@types/node@*", "@types/node@^20.9.0":
|
||||
version "20.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.13.0.tgz#011a76bc1e71ae9a026dddcfd7039084f752c4b6"
|
||||
integrity sha512-FM6AOb3khNkNIXPnHFDYaHerSv8uN22C91z098AnGccVu+Pcdhi+pNUFDi0iLmPIsVE0JBD0KVS7mzUYt4nRzQ==
|
||||
version "20.14.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420"
|
||||
integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
@@ -367,11 +372,6 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^7.5.8":
|
||||
version "7.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
|
||||
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||
|
||||
"@types/verror@^1.10.3":
|
||||
version "1.10.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
|
||||
@@ -384,64 +384,62 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.10.tgz#a102e40da7b72a2981cb2da43064d9b3c865ca58"
|
||||
integrity sha512-jsNKqn41nIS8jz5Li5xsueGEBBmRYLaflUKlclEkj8cWrO1tMK1/7xITeiVz7ZlNFZF2nop2NlXrbLtRpLEzhg==
|
||||
"@typescript-eslint/eslint-plugin@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.39.tgz#1cfd6fe38752ed56d37170d307c48a34c15f65de"
|
||||
integrity sha512-ILv1vDA8M9ah1vzYpnOs4UOLRdB63Ki/rsxedVikjMLq68hFfpsDR25bdMZ4RyUkzLJwOhcg3Jujm/C1nupXKA==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/type-utils" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.10"
|
||||
debug "^4.3.4"
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/type-utils" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.39"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.3.1"
|
||||
natural-compare "^1.4.0"
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/parser@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.0.0-alpha.10.tgz#fbefd39da010d65407b985f2b5c6e0a79bc8a6f4"
|
||||
integrity sha512-4EerPviLfBKgExHARehJgWrCtX2a7+PXBc0LBPlH93ypSgj0LU1ejMgjrB0gcfd6bJ7LN/UGNAAy3B7/Y785sA==
|
||||
"@typescript-eslint/parser@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.0.0-alpha.39.tgz#33fbd1e3767b4477def582ce597b6cd09bb5ef11"
|
||||
integrity sha512-5k+pwV91plJojHgZkWlq4/TQdOrnEaeSvt48V0m8iEwdMJqX/63BXYxy8BUOSghWcjp05s73vy9HJjovAKmHkQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.39"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.10.tgz#25506ce51ab64e99f2bc0b7d3f0f82656e14a794"
|
||||
integrity sha512-SUU0yhqehjuWilWRJWfhcxf6eMKVrZ3bpV2w6NF6GmBHR3FJo6oWZYLVXP04s6//INxpC2ynvKSglo4LRzWVTw==
|
||||
"@typescript-eslint/scope-manager@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.39.tgz#1778198dcf95175b76631f7ffd84917bf925a1a9"
|
||||
integrity sha512-HCBlKQROY+JIgWolucdFMj1W3VUnnIQTdxAhxJTAj3ix2nASmvKIFgrdo5KQMrXxQj6tC4l3zva10L+s0dUIIw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.39"
|
||||
|
||||
"@typescript-eslint/type-utils@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.10.tgz#d27f0fdd81450380887b3a07297440ba3588a70e"
|
||||
integrity sha512-6aTcbnDZWKgKr3gquECJSFyvXWLSKtUHrk2ZXDP4DEzmzTDjrkY7tIQpqv4SczPQJ+3/aky3ArPhtnQYJbAMzg==
|
||||
"@typescript-eslint/type-utils@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.39.tgz#48bcc5dc978173e02c44d7137d16edb97042c02d"
|
||||
integrity sha512-alO13fRU6yVeJbwl9ESI3AYhq5dQdz3Dpd0I5B4uezs2lvgYp44dZsj5hWyPz/kL7JFEsjbn+4b/CZA0OQJzjA==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.39"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/types@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.0.0-alpha.10.tgz#89be400c6a1751fe86f5917ed8087ec100e002da"
|
||||
integrity sha512-prbN+b/I4yH6H43WmyenMz8K5e34Hs73BQuWXR4wwij3Cg2xNGLPcpjr2cKWKlH4dZQPTz6R6oBeC+LfaoKi8g==
|
||||
"@typescript-eslint/types@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.0.0-alpha.39.tgz#e0f7618c17f03fc23803269807c77ce1bac276f2"
|
||||
integrity sha512-yINN7j0/+S1VGSp0IgH52oQvUx49vkOug6xbrDA/9o+U55yCAQKSvYWvzYjNa+SZE3hXI0zwvYtMVsIAAMmKIQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.10.tgz#e850056d2a5029688269a60206dec3bbd7beb953"
|
||||
integrity sha512-8wBUIhu6IRa440hv5/0ZEnb5JLp/UsfzIXYKRwICUOMTVj2ss1n+w3m1CtT5ghVWy5Z05qkscsbhlKFmZguU8w==
|
||||
"@typescript-eslint/typescript-estree@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.39.tgz#460b2303e3c919cb3baf4ff5a13b6fb148da70b3"
|
||||
integrity sha512-S8gREuP8r8PCxGegeojeXntx0P50ul9YH7c7JYpbLIIsEPNr5f7UHlm+I1NUbL04CBin4kvZ60TG4eWr/KKN9A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/visitor-keys" "8.0.0-alpha.39"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
@@ -449,25 +447,22 @@
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/utils@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.0.0-alpha.10.tgz#b77f743227353bfa493e95409c0e079044c9258e"
|
||||
integrity sha512-WZyNf49CuvaW/whz/B8XjYwXE/wm/EQAXq+Vqgp6BrJb8SC3bMCwGuUxReNDN1o+dNdOC96ofVSvqa8NUQ65Jg==
|
||||
"@typescript-eslint/utils@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.0.0-alpha.39.tgz#fb27dc504d7fe47fd7e1162b5012e4ce883b40b9"
|
||||
integrity sha512-Nr2PrlfNhrNQTlFHlD7XJdTGw/Vt8qY44irk6bfjn9LxGdSG5e4c1R2UN6kvGMhhx20DBPbM7q3Z3r+huzmL1w==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@types/json-schema" "^7.0.15"
|
||||
"@types/semver" "^7.5.8"
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.10"
|
||||
semver "^7.6.0"
|
||||
"@typescript-eslint/scope-manager" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/typescript-estree" "8.0.0-alpha.39"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.0.0-alpha.10":
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.10.tgz#d0a9250c69cc2f73c7f423c36183d222a329e260"
|
||||
integrity sha512-UohTNnT7S29uQgXsGZY489nWmoBBSJucNdRvog62R1QX9pQQb2pKVV1kHepUxoY2vd+M4tb9SQwZQ3gPNgqQ6w==
|
||||
"@typescript-eslint/visitor-keys@8.0.0-alpha.39":
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.39.tgz#8bf3938fddad4a00eb354880880237abfb55bda5"
|
||||
integrity sha512-DVJ0UdhucZy+/1GlIy7FX2+CFhCeNAi4VwaEAe7u2UDenQr9/kGqvzx00UlpWibmEVDw4KsPOI7Aqa1+2Vqfmw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/types" "8.0.0-alpha.39"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@xmldom/xmldom@^0.8.8":
|
||||
@@ -485,10 +480,10 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.11.3:
|
||||
version "8.11.3"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
|
||||
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
||||
acorn@^8.12.0:
|
||||
version "8.12.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
|
||||
agent-base@6, agent-base@^6.0.2:
|
||||
version "6.0.2"
|
||||
@@ -1102,14 +1097,14 @@ debounce-fn@^4.0.0:
|
||||
dependencies:
|
||||
mimic-fn "^3.0.0"
|
||||
|
||||
debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3:
|
||||
debug@4, debug@^4.3.3:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.4:
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
|
||||
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
|
||||
@@ -1325,9 +1320,9 @@ electron-updater@^6.2:
|
||||
tiny-typed-emitter "^2.1.0"
|
||||
|
||||
electron@^30:
|
||||
version "30.0.9"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-30.0.9.tgz#b11400e4642a4b635e79244ba365f1d401ee60b5"
|
||||
integrity sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow==
|
||||
version "30.1.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-30.1.2.tgz#9c8b9b0d0e3f07783d8c5dbd9519b3ffd11f1551"
|
||||
integrity sha512-A5CFGwbA+HSXnzwjc8fP2GIezBcAb0uN/VbNGLOW8DHOYn07rvJ/1bAJECHUUzt5zbfohveG3hpMQiYpbktuDw==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
@@ -1407,16 +1402,16 @@ eslint-visitor-keys@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
||||
|
||||
eslint@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.4.0.tgz#79150c3610ae606eb131f1d648d5f43b3d45f3cd"
|
||||
integrity sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA==
|
||||
eslint@^9:
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.6.0.tgz#9f54373afa15e1ba356656a8d96233182027fb49"
|
||||
integrity sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@eslint-community/regexpp" "^4.6.1"
|
||||
"@eslint/config-array" "^0.15.1"
|
||||
"@eslint/config-array" "^0.17.0"
|
||||
"@eslint/eslintrc" "^3.1.0"
|
||||
"@eslint/js" "9.4.0"
|
||||
"@eslint/js" "9.6.0"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
"@humanwhocodes/retry" "^0.3.0"
|
||||
"@nodelib/fs.walk" "^1.2.8"
|
||||
@@ -1427,8 +1422,8 @@ eslint@^9.4.0:
|
||||
escape-string-regexp "^4.0.0"
|
||||
eslint-scope "^8.0.1"
|
||||
eslint-visitor-keys "^4.0.0"
|
||||
espree "^10.0.1"
|
||||
esquery "^1.4.2"
|
||||
espree "^10.1.0"
|
||||
esquery "^1.5.0"
|
||||
esutils "^2.0.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
file-entry-cache "^8.0.0"
|
||||
@@ -1447,16 +1442,16 @@ eslint@^9.4.0:
|
||||
strip-ansi "^6.0.1"
|
||||
text-table "^0.2.0"
|
||||
|
||||
espree@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f"
|
||||
integrity sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==
|
||||
espree@^10.0.1, espree@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56"
|
||||
integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==
|
||||
dependencies:
|
||||
acorn "^8.11.3"
|
||||
acorn "^8.12.0"
|
||||
acorn-jsx "^5.3.2"
|
||||
eslint-visitor-keys "^4.0.0"
|
||||
|
||||
esquery@^1.4.2:
|
||||
esquery@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
|
||||
integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
|
||||
@@ -1905,11 +1900,6 @@ hosted-git-info@^4.1.0:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
html-entities@^2.5:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f"
|
||||
integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==
|
||||
|
||||
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
@@ -2135,11 +2125,6 @@ jake@^10.8.5:
|
||||
filelist "^1.0.4"
|
||||
minimatch "^3.1.2"
|
||||
|
||||
jpeg-js@^0.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
|
||||
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
@@ -2271,7 +2256,12 @@ lowercase-keys@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lru-cache@^10.2, lru-cache@^10.2.0:
|
||||
lru-cache@^10.2:
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b"
|
||||
integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==
|
||||
|
||||
lru-cache@^10.2.0:
|
||||
version "10.2.2"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878"
|
||||
integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==
|
||||
@@ -2774,9 +2764,9 @@ prettier-plugin-packagejson@^2:
|
||||
synckit "0.9.0"
|
||||
|
||||
prettier@^3:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.0.tgz#d173ea0524a691d4c0b1181752f2b46724328cdf"
|
||||
integrity sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a"
|
||||
integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==
|
||||
|
||||
progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
@@ -3341,14 +3331,14 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
|
||||
|
||||
typescript-eslint@8.0.0-alpha.10:
|
||||
version "8.0.0-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.0.0-alpha.10.tgz#2172d41ab30c8447927c3823c5a549b9c09be89f"
|
||||
integrity sha512-iMbN7boDtUmcSDor/J022+H4G018W3r3RSUUr7yoghMTmFuKVIkI89xJHDg82DBGYkA0xOoDNPBr7XfRFbEXKQ==
|
||||
typescript-eslint@^8.0.0-alpha.39:
|
||||
version "8.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.0.0-alpha.39.tgz#6b5eac89a47b2f6fed449cf45502d2c5e4909745"
|
||||
integrity sha512-bsuR1BVJfHr7sBh7Cca962VPIcP+5UWaIa/+6PpnFZ+qtASjGTxKWIF5dG2o73BX9NsyqQfvRWujb3M9CIoRXA==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/parser" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.10"
|
||||
"@typescript-eslint/eslint-plugin" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/parser" "8.0.0-alpha.39"
|
||||
"@typescript-eslint/utils" "8.0.0-alpha.39"
|
||||
|
||||
typescript@^5:
|
||||
version "5.5.3"
|
||||
|
||||
@@ -1,73 +1,77 @@
|
||||
---
|
||||
title: Security and privacy FAQ
|
||||
description:
|
||||
Frequently asked questions about security and privacy of Ente Photos
|
||||
title: Security and Privacy FAQ
|
||||
description: Comprehensive information about security and privacy measures in Ente Photos
|
||||
---
|
||||
|
||||
# Security and privacy
|
||||
# Security and Privacy FAQ
|
||||
|
||||
## Can Ente see my photos and videos?
|
||||
Welcome to Ente Photos' Security and Privacy FAQ. This document provides
|
||||
detailed information about our security practices, privacy measures, and how we
|
||||
protect your data. We are committed to maintaining the highest standards of data
|
||||
protection and transparency.
|
||||
|
||||
No.
|
||||
## Data Encryption and Storage
|
||||
|
||||
Your files are encrypted with a key before they are uploaded to our servers.
|
||||
### Can Ente see my photos and videos?
|
||||
No. Your files are encrypted on your device before being uploaded to our
|
||||
servers. The encryption keys are derived from your password using advanced key
|
||||
derivation functions. Since only you know your password, only you can decrypt
|
||||
your files. For technical details, please see our [architecture
|
||||
document](https://ente.io/architecture).
|
||||
|
||||
These keys can be accessed only with your password.
|
||||
### How is my data encrypted?
|
||||
We use the following encryption algorithms:
|
||||
- Encryption: `XChaCha20` and `XSalsa20`
|
||||
- Authentication: Poly1305 message authentication code (MAC)
|
||||
- Key derivation: Argon2id with high memory and computation parameters
|
||||
|
||||
Since only you know your password, only you can decrypt your files.
|
||||
These algorithms are implemented using
|
||||
[libsodium](https://libsodium.gitbook.io/doc/), a externally audited
|
||||
cryptographic library. Our [architecture document](https://ente.io/architecture)
|
||||
provides full technical specifications.
|
||||
|
||||
To learn more about our encryption protocol, please read about our
|
||||
[architecture](https://ente.io/architecture).
|
||||
### Where is my data stored?
|
||||
Your encrypted data is stored redundantly across multiple providers in the EU:
|
||||
- Amsterdam, Netherlands
|
||||
- Paris, France
|
||||
- Frankfurt, Germany
|
||||
|
||||
## How is my data encrypted?
|
||||
We use a combination of object storage and distributed databases to ensure high
|
||||
availability and durability. Our [reliability
|
||||
document](https://ente.io/reliability) provides in-depth information about our
|
||||
storage infrastructure and data replication strategies.
|
||||
|
||||
We use [libsodium](https://libsodium.gitbook.io/doc/)'s implementations
|
||||
`XChaCha20` and `XSalsa20` to encrypt your data, along with `Poly1305` MAC for
|
||||
authentication.
|
||||
### How does Ente's encryption compare to industry standards?
|
||||
Our encryption model goes beyond industry standards. While many services use
|
||||
server-side encryption, we implement end-to-end encryption. This means that even
|
||||
in the unlikely event of a server breach, your data remains protected.
|
||||
|
||||
Please refer to the document on our [architecture](https://ente.io/architecture)
|
||||
for more details.
|
||||
## Account Security
|
||||
|
||||
## Where is my data stored?
|
||||
### What happens if I forget my password?
|
||||
You can reset your password using your recovery key. This key is a randomly
|
||||
generated string provided to you during account creation. Store it securely, as
|
||||
it's your lifeline if you forget your password. If you lose both your password
|
||||
and recovery key, we cannot recover your account or data due to our
|
||||
zero-knowledge architecture.
|
||||
|
||||
Your data is replicated to multiple providers in different countries in the EU.
|
||||
|
||||
Currently we have datacenters in the following locations:
|
||||
|
||||
- Amsterdam, Netherlands
|
||||
- Paris, France
|
||||
- Frankfurt, Germany
|
||||
|
||||
Much more details about our replication and reliability are documented
|
||||
[here](https://ente.io/reliability).
|
||||
|
||||
## What happens if I forget my password?
|
||||
|
||||
You can reset your password with your recovery key.
|
||||
|
||||
If you lose both your password and your recovery key, you will not be able to
|
||||
decrypt your data.
|
||||
|
||||
## Can I change my password?
|
||||
|
||||
Yes.
|
||||
|
||||
You can change your password from any of our apps.
|
||||
|
||||
Thanks to our [architecture](https://ente.io/architecture), you can do so
|
||||
without having to re-encrypt any of your files.
|
||||
### Can I change my password?
|
||||
Yes, you can change your password at any time from our apps. Our architecture
|
||||
allows password changes without re-encrypting your entire library.
|
||||
|
||||
The privacy of your account is a function of the strength of your password,
|
||||
please choose a strong one.
|
||||
|
||||
## Do you support 2FA?
|
||||
### Do you support two-factor authentication (2FA)?
|
||||
Yes, we recommend enabling 2FA for an additional layer of security. We support:
|
||||
- Time-based One-Time Passwords (TOTP)
|
||||
- WebAuthn/FIDO2 for hardware security keys
|
||||
|
||||
Yes.
|
||||
You can set up 2FA in the settings of our mobile or desktop apps.
|
||||
|
||||
You can setup two-factor authentication from the settings screen of the mobile
|
||||
app or from the side bar of our desktop app.
|
||||
## Sharing and Collaboration
|
||||
|
||||
## How does sharing work?
|
||||
### How does sharing work?
|
||||
|
||||
The information required to decrypt an album is encrypted with the recipient's
|
||||
public key such that only they can decrypt them.
|
||||
@@ -81,22 +85,31 @@ and is never sent to our servers.
|
||||
Please note that only users on the paid plan are allowed to share albums. The
|
||||
receiver just needs a free Ente account.
|
||||
|
||||
## Has the Ente Photos app been audited by a credible source?
|
||||
## Security Audits
|
||||
|
||||
## Has the Ente Photos app been audited by a credible source?
|
||||
Yes, Ente Photos has undergone a thorough security audit conducted by Cure53, in
|
||||
collaboration with Symbolic Software. Cure53 is a prominent German cybersecurity
|
||||
firm, while Symbolic Software specializes in applied cryptography. Please find
|
||||
the full report here: https://ente.io/blog/cryptography-audit/
|
||||
|
||||
## How can I delete my account?
|
||||
## Account Management
|
||||
|
||||
### How can I delete my account?
|
||||
|
||||
You can delete your account at any time by using the "Delete account" option in
|
||||
the settings. For security reasons, we request you to delete your account on
|
||||
your own instead of contacting support to ask them to delete your account.
|
||||
|
||||
Note that both Ente photos and Ente auth data will be deleted when you delete
|
||||
Note that both Ente Photos and Ente Auth data will be deleted when you delete
|
||||
your account (irrespective of which app you delete it from) since both photos
|
||||
and auth use the same underlying account.
|
||||
|
||||
To know details of how your data is deleted, including when you delete your
|
||||
account, please see https://ente.io/blog/how-ente-deletes-data/.
|
||||
|
||||
## Additional Support
|
||||
|
||||
For any security or privacy questions not covered here, please contact our team
|
||||
at security@ente.io. We're committed to addressing your concerns and
|
||||
continuously improving our security measures.
|
||||
|
||||
@@ -5,41 +5,41 @@ description: How to report bugs and share the logs from your Ente Photos app
|
||||
|
||||
# Sharing debug logs
|
||||
|
||||
In some cases when you report a bug, our customer support might request you to
|
||||
share debug logs from your app to help our developers find the issue.
|
||||
In some cases when you report an issue, our customer support might request you
|
||||
to share debug logs from your app to help our developers find the issue.
|
||||
|
||||
Note that the debug logs contain potentially sensitive information like the file
|
||||
names, so please feel free to not share them if you have any hesitation or want
|
||||
to keep these private. We will try to diagnose the issue even without the logs,
|
||||
the logs just make the process a bit faster and easier.
|
||||
|
||||
### Mobile
|
||||
## Mobile
|
||||
|
||||
To **_Report a bug_** on your mobile device, follow these steps:
|
||||
- Open settings (tap on the three horizontal lines button).
|
||||
- Tap on _Support_ from the settings.
|
||||
- Select for the option to _Report a Bug_.
|
||||
- Tap on _Report a bug_.
|
||||
|
||||
- Tap on the three horizontal lines to access the settings.
|
||||
- Tap on **"Support"** from the settings.
|
||||
- Select for the option to **"Report a Bug"**.
|
||||
- Tap on **"Report a bug"** .
|
||||
## Desktop
|
||||
|
||||
### Desktop
|
||||
- Click on _Help_ menu at the top of your screen, and select the _View logs_
|
||||
option.
|
||||
- Open settings (click on the three horizontal lines button located at the top
|
||||
left corner of the screen).
|
||||
- Click on _Support_. This will open your email client where you can attach
|
||||
the logs in the email and describe the issue.
|
||||
|
||||
To **_Report a bug_** on the desktop app, follow these steps:
|
||||
## Web
|
||||
|
||||
- Click on the three horizontal lines located in the top left corner of the
|
||||
screen to access the settings.
|
||||
- Click on **"Debug logs"** from the settings.
|
||||
- Click on **Download logs**.
|
||||
- Then Click on **"Support"**.
|
||||
- Attach the downloaded logs in the email and describe the issue.
|
||||
- Open settings (click on the three horizontal lines button located at the top
|
||||
left corner of the screen).
|
||||
- Click on _Debug Logs_ towards the bottom of settings.
|
||||
- Click on _Download logs_
|
||||
- Click on _Support_. This will open your email client where you can attach
|
||||
the logs in the email and describe the issue.
|
||||
|
||||
### Web
|
||||
## Send email manually
|
||||
|
||||
To **_Report a bug_** on the web, follow these steps:
|
||||
|
||||
- Click on the three horizontal lines located in the top left corner of the
|
||||
screen to access the settings.
|
||||
- Click on **"Debug Logs"**
|
||||
- Click on **Download logs**
|
||||
- Click on **"Support"** from the settings.
|
||||
- Attach the downloaded logs in the email and describe the issue.
|
||||
If _Report a bug_ or _Support_ doesn't automatically open your email client, you
|
||||
can also directly send a mail to <a
|
||||
href="mailto:support@ente.io">support@ente.io</a>.
|
||||
|
||||
@@ -659,9 +659,9 @@ preact@^10.0.0:
|
||||
integrity sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==
|
||||
|
||||
prettier@^3:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368"
|
||||
integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a"
|
||||
integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==
|
||||
|
||||
rfdc@^1.3.1:
|
||||
version "1.3.1"
|
||||
|
||||
@@ -17,7 +17,7 @@ RUN \
|
||||
# Install SCW CLI
|
||||
# Latest release: https://github.com/scaleway/scaleway-cli/releases/latest
|
||||
RUN \
|
||||
export VERSION="2.26.0" && \
|
||||
export VERSION="2.32.1" && \
|
||||
curl -o /usr/local/bin/scw -L "https://github.com/scaleway/scaleway-cli/releases/download/v${VERSION}/scaleway-cli_${VERSION}_linux_amd64" && \
|
||||
chmod +x /usr/local/bin/scw
|
||||
|
||||
|
||||
@@ -3,9 +3,16 @@
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# Find the name of the latest backup
|
||||
# The backup file name contains the epoch, so we can just sort.
|
||||
BACKUP_FILE=$(rclone lsf --include 'db-*.custom' --files-only $RCLONE_DESTINATION | sort | tail -1)
|
||||
if test -z "$1"; then
|
||||
# Find the name of the latest backup.
|
||||
#
|
||||
# The backup file name contains the epoch, so we can just sort.
|
||||
BACKUP_FILE=$(rclone lsf --include 'db-*.custom' --files-only $RCLONE_DESTINATION | sort | tail -1)
|
||||
else
|
||||
# If a CLI argument is provided, use that as the name of the backup file to
|
||||
# restore.
|
||||
BACKUP_FILE="$1"
|
||||
fi
|
||||
|
||||
# Download it
|
||||
rclone copy --log-level INFO "${RCLONE_DESTINATION}${BACKUP_FILE}" .
|
||||
|
||||
@@ -61,7 +61,7 @@ const handleGET = async (request: Request) => {
|
||||
const params = new URLSearchParams({ castToken });
|
||||
|
||||
let response = await fetch(
|
||||
`https://api.ente.io/cast/files${pathname}${fileID}?${params.toString()}`
|
||||
`https://api.ente.io/cast/files${pathname}${fileID}?${params.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) console.log("Upstream error", response.status);
|
||||
|
||||
@@ -17,16 +17,12 @@ export default {
|
||||
const handleOPTIONS = (request: Request) => {
|
||||
const origin = request.headers.get("Origin");
|
||||
if (!isAllowedOrigin(origin)) console.warn("Unknown origin", origin);
|
||||
const headers = request.headers.get("Access-Control-Request-Headers");
|
||||
if (!areAllowedHeaders(headers))
|
||||
console.warn("Unknown header in list", headers);
|
||||
return new Response("", {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
// "Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -48,16 +44,6 @@ const isAllowedOrigin = (origin: string | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
const areAllowedHeaders = (headers: string | null) => {
|
||||
const allowed = ["x-auth-token", "x-client-package"];
|
||||
|
||||
if (!headers) return true;
|
||||
for (const header of headers.split(",")) {
|
||||
if (!allowed.includes(header.trim().toLowerCase())) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleGET = async (request: Request) => {
|
||||
const url = new URL(request.url);
|
||||
|
||||
@@ -91,7 +77,7 @@ const handleGET = async (request: Request) => {
|
||||
headers: {
|
||||
"User-Agent": request.headers.get("User-Agent") ?? "",
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) console.log("Upstream error", response.status);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240614.0",
|
||||
"typescript": "^5",
|
||||
"wrangler": "^3"
|
||||
"wrangler": "^3",
|
||||
"prettier": "^3"
|
||||
},
|
||||
"workspaces": [
|
||||
"*"
|
||||
|
||||
@@ -17,17 +17,13 @@ export default {
|
||||
const handleOPTIONS = (request: Request) => {
|
||||
const origin = request.headers.get("Origin");
|
||||
if (!isAllowedOrigin(origin)) console.warn("Unknown origin", origin);
|
||||
const headers = request.headers.get("Access-Control-Request-Headers");
|
||||
if (!areAllowedHeaders(headers))
|
||||
console.warn("Unknown header in list", headers);
|
||||
return new Response("", {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers":
|
||||
"X-Auth-Access-Token, X-Auth-Access-Token-JWT, X-Client-Package",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
// "Access-Control-Allow-Headers": "X-Auth-Access-Token, X-Auth-Access-Token-JWT",
|
||||
// "Access-Control-Allow-Headers": "X-Auth-Access-Token, X-Auth-Access-Token-JWT, x-client-package",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -45,21 +41,6 @@ const isAllowedOrigin = (origin: string | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
const areAllowedHeaders = (headers: string | null) => {
|
||||
// TODO(MR): Stop sending "x-client-package"
|
||||
const allowed = [
|
||||
"x-auth-access-token",
|
||||
"x-auth-access-token-jwt",
|
||||
"x-client-package",
|
||||
];
|
||||
|
||||
if (!headers) return true;
|
||||
for (const header of headers.split(",")) {
|
||||
if (!allowed.includes(header.trim().toLowerCase())) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleGET = async (request: Request) => {
|
||||
const url = new URL(request.url);
|
||||
|
||||
@@ -90,7 +71,7 @@ const handleGET = async (request: Request) => {
|
||||
if (accessTokenJWT) params.set("accessTokenJWT", accessTokenJWT);
|
||||
|
||||
let response = await fetch(
|
||||
`https://api.ente.io/public-collection/files${pathname}${fileID}?${params.toString()}`
|
||||
`https://api.ente.io/public-collection/files${pathname}${fileID}?${params.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) console.log("Upstream error", response.status);
|
||||
|
||||
@@ -17,16 +17,12 @@ export default {
|
||||
const handleOPTIONS = (request: Request) => {
|
||||
const origin = request.headers.get("Origin");
|
||||
if (!isAllowedOrigin(origin)) console.warn("Unknown origin", origin);
|
||||
const headers = request.headers.get("Access-Control-Request-Headers");
|
||||
if (!areAllowedHeaders(headers))
|
||||
console.warn("Unknown header in list", headers);
|
||||
return new Response("", {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
// "Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -48,16 +44,6 @@ const isAllowedOrigin = (origin: string | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
const areAllowedHeaders = (headers: string | null) => {
|
||||
const allowed = ["x-auth-token", "x-client-package"];
|
||||
|
||||
if (!headers) return true;
|
||||
for (const header of headers.split(",")) {
|
||||
if (!allowed.includes(header.trim().toLowerCase())) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleGET = async (request: Request) => {
|
||||
const url = new URL(request.url);
|
||||
|
||||
@@ -79,7 +65,7 @@ const handleGET = async (request: Request) => {
|
||||
if (token) params.set("token", token);
|
||||
|
||||
let response = await fetch(
|
||||
`https://api.ente.io/files/preview/${fileID}?${params.toString()}`
|
||||
`https://api.ente.io/files/preview/${fileID}?${params.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) console.log("Upstream error", response.status);
|
||||
|
||||
@@ -23,17 +23,14 @@ export default {
|
||||
const handleOPTIONS = (request: Request) => {
|
||||
const origin = request.headers.get("Origin");
|
||||
if (!isAllowedOrigin(origin)) console.warn("Unknown origin", origin);
|
||||
const headers = request.headers.get("Access-Control-Request-Headers");
|
||||
if (!areAllowedHeaders(headers))
|
||||
console.warn("Unknown header in list", headers);
|
||||
return new Response("", {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "POST, PUT, OPTIONS",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
// "Access-Control-Allow-Headers": "Content-Type", "UPLOAD-URL, X-Client-Package",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Headers":
|
||||
"Content-Type, UPLOAD-URL, X-Client-Package",
|
||||
"Access-Control-Expose-Headers": "X-Request-Id, CF-Ray",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -55,16 +52,6 @@ const isAllowedOrigin = (origin: string | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
const areAllowedHeaders = (headers: string | null) => {
|
||||
const allowed = ["content-type", "upload-url", "x-client-package"];
|
||||
|
||||
if (!headers) return true;
|
||||
for (const header of headers.split(",")) {
|
||||
if (!allowed.includes(header.trim().toLowerCase())) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handlePOSTOrPUT = async (request: Request) => {
|
||||
const url = new URL(request.url);
|
||||
|
||||
@@ -116,7 +103,7 @@ const handlePOSTOrPUT = async (request: Request) => {
|
||||
response.headers.set("Access-Control-Allow-Origin", "*");
|
||||
response.headers.set(
|
||||
"Access-Control-Expose-Headers",
|
||||
"X-Request-Id, CF-Ray"
|
||||
"X-Request-Id, CF-Ray",
|
||||
);
|
||||
return response;
|
||||
};
|
||||
|
||||
36
mobile/fastlane/metadata/android/pl/full_description.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
Ente to prosta aplikacja do tworzenia kopii zapasowej Twoich zdjęć i filmów.
|
||||
|
||||
Jeśli szukałeś/aś przyjaznej dla prywatności alternatywy do Google Photos to dotarłeś/aś do właściwego miejsca. Dzięki Ente są one przechowywane w pełni zaszyfrowane (e2ee). Oznacza to, że tylko Ty możesz je oglądać.
|
||||
|
||||
Mamy otwarto-źródłowe aplikacje na wszystkich platformach, a Twoje zdjęcia będą płynnie synchronizowane między Twoimi urządzeniami w sposób szyfrowany end-to-end (e2ee).
|
||||
|
||||
Ente sprawia, że udostępnianie twoich albumów swoim bliskim również jest proste, nawet jeśli nie są w Ente. Możesz udostępniać publicznie widoczne linki, gdzie mogą oglądać twój album i współpracować, dodając do niego zdjęcia, nawet bez konta lub aplikacji.
|
||||
|
||||
Twoje zaszyfrowane dane są przechowywane w wielu lokalizacjach, między innymi w schronie przeciwatomowym w Paryżu. Poważnie podchodzimy do kwestii zachowania pamięci i ułatwiamy zadbanie o to, by wspomnienia przetrwały dłużej niż ty sam.
|
||||
|
||||
Jesteśmy tutaj, aby zrobić najbezpieczniejszą aplikację na zdjęcia, dołącz do naszej podróży!
|
||||
|
||||
FUNKCJE
|
||||
- Oryginalne kopie zapasowe wysokiej jakości, ponieważ każdy piksel jest ważny
|
||||
- Plany rodzinne, umożliwiające współdzielenie pamięci z rodziną
|
||||
- Wspólne albumy, dzięki czemu możesz zbierać zdjęcia po podróży
|
||||
- Udostępnione foldery, jeśli chcesz, aby Twój partner cieszył się kliknięciami "Aparatu"
|
||||
- Linki do albumów, które mogą być chronione hasłem
|
||||
- Możliwość zwolnienia miejsca poprzez usunięcie plików, które zostały bezpiecznie zarchiwizowane
|
||||
- Wsparcie ludzkie, ponieważ jesteś tego warty/a
|
||||
- Opisy, dzięki którym możesz podpisać swoje wspomnienia i łatwo je znaleźć
|
||||
- Edytor obrazów, aby dodać ostatnie poprawki
|
||||
- Dodaj do ulubionych, ukryj i przeżyj swoje wspomnienia, ponieważ są cenne
|
||||
- Import jednym kliknięciem z Google, Apple, dysku twardego i więcej
|
||||
- Ciemny motyw, ponieważ Twoje zdjęcia wyglądają w nim dobrze
|
||||
- 2FA, 3FA, uwierzytelnianie biometryczne
|
||||
- i o WIELE więcej!
|
||||
|
||||
UPRAWNIENIA
|
||||
Ente prosi o określone uprawnienia, aby służyć jako dostawca usług przechowywania zdjęć, które można przejrzeć tutaj: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md
|
||||
|
||||
CENNIK
|
||||
Nie oferujemy wiecznie darmowych planów, ponieważ ważne jest dla nas, abyśmy pozostali zrównoważeni i wytrzymali próbę czasu. Zamiast tego oferujemy przystępne cenowo plany, którymi można swobodnie dzielić się z rodziną. Więcej informacji możesz znaleźć na stronie ente.io.
|
||||
|
||||
WSPARCIE
|
||||
Jesteśmy dumni z tego, że oferujemy ludzkie wsparcie. Jeśli jesteś naszym płatnym klientem, możesz skontaktować się z nami pod adresem team@ente.io i oczekiwać odpowiedzi od naszego zespołu w ciągu 24 godzin.
|
||||
@@ -1 +1 @@
|
||||
ente to w pełni szyfrowana aplikacja do przechowywania zdjęć
|
||||
Ente to w pełni szyfrowana aplikacja do przechowywania zdjęć
|
||||
@@ -1 +1 @@
|
||||
ente - szyfrowane przechowywanie zdjęć
|
||||
Ente - szyfrowane przechowywanie zdjęć
|
||||
@@ -1,8 +1,8 @@
|
||||
Ente to prosta aplikacja do automatycznego tworzenia kopii zapasowych oraz porządkowania zdjęć i filmów.
|
||||
|
||||
Jeśli szukasz przyjaznej dla prywatności alternatywy, aby zachować swoje wspomnienia, jesteś we właściwym miejscu. Dzięki Ente są one przechowywane w pełni zaszyfrowane (e2ee). Oznacza to, że tylko Ty możesz je oglądać.
|
||||
Jeśli szukałeś/aś przyjaznej dla prywatności alternatywy, aby zachować swoje wspomnienia, to dotarłeś/aś do właściwego miejsca. Dzięki Ente są one przechowywane w pełni zaszyfrowane (e2ee). Oznacza to, że tylko Ty możesz je oglądać.
|
||||
|
||||
Mamy aplikacje na wszystkich platformach, a Twoje zdjęcia będą płynnie synchronizowane między Twoimi urządzeniami w sposób szyfrowany (e2ee).
|
||||
Mamy aplikacje na wszystkich platformach, a Twoje zdjęcia będą płynnie synchronizowane między Twoimi urządzeniami w sposób szyfrowany end-to-end (e2ee).
|
||||
|
||||
Ente ułatwia również udostępnianie albumów najbliższym. Możesz udostępniać je bezpośrednio innym użytkownikom Ente, w pełni zaszyfrowane; lub za pomocą publicznie dostępnych linków.
|
||||
|
||||
@@ -23,11 +23,11 @@ FUNKCJE
|
||||
- 2FA, 3FA, uwierzytelnianie biometryczne
|
||||
- i o WIELE więcej!
|
||||
|
||||
WARUNKI
|
||||
CENNIK
|
||||
Nie oferujemy planów darmowych na zawsze, ponieważ ważne jest dla nas, abyśmy pozostali zrównoważeni i wytrzymali próbę czasu. Zamiast tego oferujemy przystępne cenowo plany, którymi można swobodnie dzielić się z rodziną. Więcej informacji możesz znaleźć na stronie ente.io.
|
||||
|
||||
WSPARCIE
|
||||
Jesteśmy dumni z tego, że oferujemy ludzkie wsparcie. Jeśli jesteś naszym płatnym klientem, możesz skontaktować się z nami pod adresem team@ente.io i oczekiwać odpowiedzi od naszego zespołu w ciągu 24 godzin.
|
||||
|
||||
CENA
|
||||
WARUNKI
|
||||
https://ente.io/terms
|
||||
|
||||
30
mobile/fastlane/metadata/playstore/pl/full_description.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
Ente to prosta aplikacja do automatycznego tworzenia kopii zapasowych oraz porządkowania zdjęć i filmów.
|
||||
|
||||
Jeśli szukałeś/aś przyjaznej dla prywatności alternatywy, aby zachować swoje wspomnienia, to dotarłeś/aś do właściwego miejsca. Dzięki Ente są one przechowywane w pełni zaszyfrowane (e2ee). Oznacza to, że tylko Ty możesz je oglądać.
|
||||
|
||||
Mamy otwarto-źródłowe aplikacje na wszystkich platformach, a Twoje zdjęcia będą płynnie synchronizowane między Twoimi urządzeniami w sposób szyfrowany end-to-end (e2ee).
|
||||
|
||||
Ente ułatwia również udostępnianie albumów najbliższym. Możesz udostępniać je bezpośrednio innym użytkownikom Ente, w pełni zaszyfrowane; lub za pomocą publicznie dostępnych linków.
|
||||
|
||||
Twoje zaszyfrowane dane są przechowywane w wielu lokalizacjach, między innymi w schronie przeciwatomowym w Paryżu. Poważnie podchodzimy do kwestii zachowania pamięci i ułatwiamy zadbanie o to, by wspomnienia przetrwały dłużej niż ty sam.
|
||||
|
||||
Jesteśmy tutaj, aby zrobić najbezpieczniejszą aplikację na zdjęcia, dołącz do naszej podróży!
|
||||
|
||||
✨ FUNKCJE
|
||||
- Kopie zapasowe oryginalnej jakości, ponieważ każdy piksel jest ważny
|
||||
- Plany rodzinne, dzięki czemu możesz udostępnić pamięć swojej rodzinie
|
||||
- Udostępnione foldery, jeśli chcesz, aby Twój partner cieszył się kliknięciami "Aparatu"
|
||||
- Linki do albumów, które mogą być chronione hasłem i automatycznie wygasać
|
||||
- Możliwość zwolnienia miejsca poprzez usunięcie plików, które zostały bezpiecznie zarchiwizowane
|
||||
- Edytor obrazów, aby dodać ostatnie poprawki
|
||||
- Dodaj do ulubionych, ukryj i przeżyj swoje wspomnienia, ponieważ są cenne
|
||||
- Import jednym kliknięciem z Google, Apple, dysku twardego i więcej
|
||||
- Ciemny motyw, ponieważ Twoje zdjęcia wyglądają w nim dobrze
|
||||
- 2FA, 3FA, uwierzytelnianie biometryczne
|
||||
- i o WIELE więcej!
|
||||
|
||||
💲 CENNIK
|
||||
Nie oferujemy wiecznie darmowych planów, ponieważ ważne jest dla nas, abyśmy pozostali zrównoważeni i wytrzymali próbę czasu. Zamiast tego oferujemy przystępne cenowo plany, którymi można swobodnie dzielić się z rodziną. Więcej informacji możesz znaleźć na stronie ente.io.
|
||||
|
||||
🙋 WSPARCIE
|
||||
Jesteśmy dumni z tego, że oferujemy ludzkie wsparcie. Jeśli jesteś naszym płatnym klientem, możesz skontaktować się z nami pod adresem team@ente.io i oczekiwać odpowiedzi od naszego zespołu w ciągu 24 godzin.
|
||||
@@ -1252,17 +1252,28 @@
|
||||
"right": "Rechts",
|
||||
"whatsNew": "Neue Funktionen",
|
||||
"reviewSuggestions": "Vorschläge überprüfen",
|
||||
"reenterPassword": "Re-enter password",
|
||||
"reenterPin": "Re-enter PIN",
|
||||
"deviceLock": "Device lock",
|
||||
"pinLock": "PIN lock",
|
||||
"next": "Next",
|
||||
"setNewPassword": "Set new password",
|
||||
"enterPin": "Enter PIN",
|
||||
"setNewPin": "Set new PIN",
|
||||
"appLock": "App lock",
|
||||
"noSystemLockFound": "No system lock found",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "To enable app lock, please setup device passcode or screen lock in your system settings.",
|
||||
"tapToUnlock": "Tap to unlock",
|
||||
"tooManyIncorrectAttempts": "Too many incorrect attempts"
|
||||
"useAsCover": "Als Titelbild festlegen",
|
||||
"notPersonLabel": "Nicht {name}?",
|
||||
"@notPersonLabel": {
|
||||
"description": "Label to indicate that the person in the photo is not the person whose name is mentioned",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "{name}",
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reenterPassword": "Passwort erneut eingeben",
|
||||
"reenterPin": "PIN erneut eingeben",
|
||||
"deviceLock": "Gerätsperre",
|
||||
"pinLock": "PIN-Sperre",
|
||||
"next": "Weiter",
|
||||
"setNewPassword": "Neues Passwort festlegen",
|
||||
"enterPin": "PIN eingeben",
|
||||
"setNewPin": "Neue PIN festlegen",
|
||||
"appLock": "App-Sperre",
|
||||
"noSystemLockFound": "Keine Systemsperre gefunden",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Um die App-Sperre zu aktivieren, konfigurieren Sie bitte den Gerätepasscode oder die Bildschirmsperre in Ihren Systemeinstellungen.",
|
||||
"tapToUnlock": "Zum Entsperren antippen",
|
||||
"tooManyIncorrectAttempts": "Zu viele fehlerhafte Versuche"
|
||||
}
|
||||
@@ -409,7 +409,7 @@
|
||||
"manageDeviceStorage": "Apparaatopslag beheren",
|
||||
"machineLearning": "Machine Learning",
|
||||
"magicSearch": "Magische zoekfunctie",
|
||||
"magicSearchDescription": "Houd er rekening mee dat dit zal resulteren in een hoger internet- en batterijverbruik totdat alle items zijn geïndexeerd.",
|
||||
"mlIndexingDescription": "Houd er rekening mee dat dit zal resulteren in een hoger internet- en batterijverbruik totdat alle items zijn geïndexeerd.",
|
||||
"loadingModel": "Modellen downloaden...",
|
||||
"waitingForWifi": "Wachten op WiFi...",
|
||||
"status": "Status",
|
||||
@@ -451,6 +451,7 @@
|
||||
"privacy": "Privacy",
|
||||
"terms": "Voorwaarden",
|
||||
"checkForUpdates": "Controleer op updates",
|
||||
"checkStatus": "Status controleren",
|
||||
"checking": "Controleren...",
|
||||
"youAreOnTheLatestVersion": "Je hebt de laatste versie",
|
||||
"account": "Account",
|
||||
@@ -478,9 +479,13 @@
|
||||
"backedUpFolders": "Back-up mappen",
|
||||
"backup": "Back-up",
|
||||
"freeUpDeviceSpace": "Apparaatruimte vrijmaken",
|
||||
"freeUpDeviceSpaceDesc": "Bespaar ruimte op je apparaat door bestanden die al geback-upt zijn te wissen.",
|
||||
"allClear": "✨ Alles in orde",
|
||||
"noDeviceThatCanBeDeleted": "Je hebt geen bestanden op dit apparaat die verwijderd kunnen worden",
|
||||
"removeDuplicates": "Duplicaten verwijderen",
|
||||
"removeDuplicatesDesc": "Controleer en verwijder bestanden die exacte kopieën zijn.",
|
||||
"viewLargeFiles": "Grote bestanden",
|
||||
"viewLargeFilesDesc": "Bekijk bestanden die de meeste opslagruimte verbruiken",
|
||||
"noDuplicates": "✨ Geen duplicaten",
|
||||
"youveNoDuplicateFilesThatCanBeCleared": "Je hebt geen dubbele bestanden die kunnen worden gewist",
|
||||
"success": "Succes",
|
||||
@@ -667,7 +672,7 @@
|
||||
"mobileWebDesktop": "Mobiel, Web, Desktop",
|
||||
"newToEnte": "Nieuw bij Ente",
|
||||
"pleaseLoginAgain": "Log opnieuw in",
|
||||
"devAccountChanged": "Het ontwikkelaarsaccount dat we gebruiken om te publiceren in de App Store is veranderd. Daarom moet je opnieuw inloggen.\n\nOnze excuses voor het ongemak, helaas was dit onvermijdelijk.",
|
||||
"autoLogoutMessage": "Door een technische storing bent u uitgelogd. Onze excuses voor het ongemak.",
|
||||
"yourSubscriptionHasExpired": "Uw abonnement is verlopen",
|
||||
"storageLimitExceeded": "Opslaglimiet overschreden",
|
||||
"upgrade": "Upgraden",
|
||||
@@ -987,7 +992,7 @@
|
||||
"fileTypesAndNames": "Bestandstypen en namen",
|
||||
"location": "Locatie",
|
||||
"moments": "Momenten",
|
||||
"searchFaceEmptySection": "Vind alle foto's van een persoon",
|
||||
"searchFaceEmptySection": "Mensen worden hier getoond als het indexeren klaar is",
|
||||
"searchDatesEmptySection": "Zoeken op een datum, maand of jaar",
|
||||
"searchLocationEmptySection": "Foto's groeperen die in een bepaalde straal van een foto worden genomen",
|
||||
"searchPeopleEmptySection": "Nodig mensen uit, en je ziet alle foto's die door hen worden gedeeld hier",
|
||||
@@ -1171,6 +1176,7 @@
|
||||
}
|
||||
},
|
||||
"faces": "Gezichten",
|
||||
"people": "Personen",
|
||||
"contents": "Inhoud",
|
||||
"addNew": "Nieuwe toevoegen",
|
||||
"@addNew": {
|
||||
@@ -1193,17 +1199,20 @@
|
||||
"waitingForVerification": "Wachten op verificatie...",
|
||||
"passkey": "Passkey",
|
||||
"passkeyAuthTitle": "Passkey verificatie",
|
||||
"passKeyPendingVerification": "Verificatie is nog in behandeling",
|
||||
"loginSessionExpired": "Sessie verlopen",
|
||||
"loginSessionExpiredDetails": "Jouw sessie is verlopen. Log opnieuw in.",
|
||||
"verifyPasskey": "Bevestig passkey",
|
||||
"playOnTv": "Album afspelen op TV",
|
||||
"pair": "Koppelen",
|
||||
"autoPair": "Automatisch koppelen",
|
||||
"pairWithPin": "Koppelen met PIN",
|
||||
"deviceNotFound": "Apparaat niet gevonden",
|
||||
"castInstruction": "Bezoek cast.ente.io op het apparaat dat u wilt koppelen.\n\nVoer de code hieronder in om het album op uw TV af te spelen.",
|
||||
"deviceCodeHint": "Voer de code in",
|
||||
"joinDiscord": "Join de Discord",
|
||||
"locations": "Locaties",
|
||||
"descriptions": "Beschrijvingen",
|
||||
"addAName": "Een naam toevoegen",
|
||||
"findPeopleByName": "Mensen snel op naam zoeken",
|
||||
"addViewers": "{count, plural, one {Voeg kijker toe} other {Voeg kijkers toe}}",
|
||||
"addCollaborators": "{count, plural, zero {Voeg samenwerker toe} one {Voeg samenwerker toe} other {Voeg samenwerkers toe}}",
|
||||
"longPressAnEmailToVerifyEndToEndEncryption": "Druk lang op een e-mail om de versleuteling te verifiëren.",
|
||||
@@ -1216,6 +1225,8 @@
|
||||
"customEndpoint": "Verbonden met {endpoint}",
|
||||
"createCollaborativeLink": "Maak een gezamenlijke link",
|
||||
"search": "Zoeken",
|
||||
"enterPersonName": "Naam van persoon invoeren",
|
||||
"removePersonLabel": "Verwijder persoonslabel",
|
||||
"autoPairDesc": "Automatisch koppelen werkt alleen met apparaten die Chromecast ondersteunen.",
|
||||
"manualPairDesc": "Koppelen met de PIN werkt met elk scherm waarop je jouw album wilt zien.",
|
||||
"connectToDevice": "Verbinding maken met apparaat",
|
||||
@@ -1227,22 +1238,42 @@
|
||||
"castIPMismatchTitle": "Album casten mislukt",
|
||||
"castIPMismatchBody": "Zorg ervoor dat je op hetzelfde netwerk zit als de tv.",
|
||||
"pairingComplete": "Koppeling voltooid",
|
||||
"faceRecognition": "Face recognition",
|
||||
"faceRecognitionIndexingDescription": "Please note that this will result in a higher bandwidth and battery usage until all items are indexed.",
|
||||
"foundFaces": "Found faces",
|
||||
"clusteringProgress": "Clustering progress",
|
||||
"indexingIsPaused": "Indexing is paused, will automatically resume when device is ready",
|
||||
"reenterPassword": "Re-enter password",
|
||||
"reenterPin": "Re-enter PIN",
|
||||
"deviceLock": "Device lock",
|
||||
"pinLock": "PIN lock",
|
||||
"next": "Next",
|
||||
"setNewPassword": "Set new password",
|
||||
"enterPin": "Enter PIN",
|
||||
"setNewPin": "Set new PIN",
|
||||
"appLock": "App lock",
|
||||
"noSystemLockFound": "No system lock found",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "To enable app lock, please setup device passcode or screen lock in your system settings.",
|
||||
"tapToUnlock": "Tap to unlock",
|
||||
"tooManyIncorrectAttempts": "Too many incorrect attempts"
|
||||
"savingEdits": "Bewerken opslaan...",
|
||||
"autoPair": "Automatisch koppelen",
|
||||
"pairWithPin": "Koppelen met PIN",
|
||||
"faceRecognition": "Gezichtsherkenning",
|
||||
"foundFaces": "Gezichten gevonden",
|
||||
"clusteringProgress": "Voortgang clusteren",
|
||||
"indexingIsPaused": "Indexeren is gepauzeerd. Het zal automatisch hervatten wanneer het apparaat klaar is.",
|
||||
"trim": "Knippen",
|
||||
"crop": "Bijsnijden",
|
||||
"rotate": "Roteren",
|
||||
"left": "Links",
|
||||
"right": "Rechts",
|
||||
"whatsNew": "Nieuw",
|
||||
"reviewSuggestions": "Suggesties beoordelen",
|
||||
"useAsCover": "Als cover gebruiken",
|
||||
"notPersonLabel": "Niet {name}?",
|
||||
"@notPersonLabel": {
|
||||
"description": "Label to indicate that the person in the photo is not the person whose name is mentioned",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "{name}",
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reenterPassword": "Wachtwoord opnieuw invoeren",
|
||||
"reenterPin": "PIN opnieuw invoeren",
|
||||
"deviceLock": "Apparaat vergrendeld",
|
||||
"pinLock": "PIN vergrendeling",
|
||||
"next": "Volgende",
|
||||
"setNewPassword": "Nieuw wachtwoord instellen",
|
||||
"enterPin": "PIN invoeren",
|
||||
"setNewPin": "Nieuwe PIN instellen",
|
||||
"appLock": "App-vergrendeling",
|
||||
"noSystemLockFound": "Geen systeemvergrendeling gevonden",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Om vergrendelscherm in te schakelen, moet u een toegangscode of schermvergrendeling instellen in uw systeeminstellingen.",
|
||||
"tapToUnlock": "Tik om te ontgrendelen",
|
||||
"tooManyIncorrectAttempts": "Te veel onjuiste pogingen"
|
||||
}
|
||||
@@ -48,8 +48,8 @@
|
||||
"sorry": "Desculpe",
|
||||
"noRecoveryKeyNoDecryption": "Devido à natureza do nosso protocolo de criptografia de ponta a ponta, seus dados não podem ser descriptografados sem sua senha ou chave de recuperação",
|
||||
"verifyEmail": "Verificar e-mail",
|
||||
"toResetVerifyEmail": "Para redefinir a sua senha, por favor verifique o seu email primeiro.",
|
||||
"checkInboxAndSpamFolder": "Verifique sua caixa de entrada (e ‘spam’) para concluir a verificação",
|
||||
"toResetVerifyEmail": "Para redefinir a sua senha, por favor verifique o seu e-mail primeiro.",
|
||||
"checkInboxAndSpamFolder": "Verifique sua caixa de entrada (e spam) para concluir a verificação",
|
||||
"tapToEnterCode": "Toque para inserir código",
|
||||
"resendEmail": "Reenviar e-mail",
|
||||
"weHaveSendEmailTo": "Enviamos um e-mail à <green>{email}</green>",
|
||||
@@ -153,7 +153,7 @@
|
||||
"confirmYourRecoveryKey": "Confirme sua chave de recuperação",
|
||||
"addViewer": "Adicionar visualizador",
|
||||
"addCollaborator": "Adicionar colaborador",
|
||||
"addANewEmail": "Adicionar um novo email",
|
||||
"addANewEmail": "Adicionar um novo e-mail",
|
||||
"orPickAnExistingOne": "Ou escolha um existente",
|
||||
"collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": "Os colaboradores podem adicionar fotos e vídeos ao álbum compartilhado.",
|
||||
"enterEmail": "Insira o e-mail",
|
||||
@@ -284,7 +284,7 @@
|
||||
"shareTextReferralCode": "Código de referência do ente: {referralCode} \n\nAplique em Configurações → Geral → Indicações para obter {referralStorageInGB} GB gratuitamente após a sua inscrição em um plano pago\n\nhttps://ente.io",
|
||||
"claimFreeStorage": "Reivindicar armazenamento gratuito",
|
||||
"inviteYourFriends": "Convide seus amigos",
|
||||
"failedToFetchReferralDetails": "Não foi possível buscar informações do produto. Por favor, tente novamente mais tarde.",
|
||||
"failedToFetchReferralDetails": "Não foi possível buscar os detalhes de referência. Por favor, tente novamente mais tarde.",
|
||||
"referralStep1": "Envie esse código aos seus amigos",
|
||||
"referralStep2": "2. Eles se inscreveram para um plano pago",
|
||||
"referralStep3": "3. Ambos ganham {storageInGB} GB* grátis",
|
||||
@@ -476,7 +476,7 @@
|
||||
"cannotDeleteSharedFiles": "Não é possível excluir arquivos compartilhados",
|
||||
"theDownloadCouldNotBeCompleted": "Não foi possível concluir o download",
|
||||
"retry": "Tentar novamente",
|
||||
"backedUpFolders": "Backup de pastas concluído",
|
||||
"backedUpFolders": "Pastas com backup",
|
||||
"backup": "Backup",
|
||||
"freeUpDeviceSpace": "Liberar espaço no dispositivo",
|
||||
"freeUpDeviceSpaceDesc": "Economize espaço no seu dispositivo limpando arquivos que já foram salvos em backup.",
|
||||
@@ -947,7 +947,7 @@
|
||||
"thisActionCannotBeUndone": "Esta ação não pode ser desfeita",
|
||||
"emptyTrash": "Esvaziar a lixeira?",
|
||||
"permDeleteWarning": "Todos os itens na lixeira serão excluídos permanentemente\n\nEsta ação não pode ser desfeita",
|
||||
"empty": "Vazio",
|
||||
"empty": "Esvaziar",
|
||||
"couldNotFreeUpSpace": "Não foi possível liberar espaço",
|
||||
"permanentlyDeleteFromDevice": "Excluir permanentemente do dispositivo?",
|
||||
"someOfTheFilesYouAreTryingToDeleteAre": "Alguns dos arquivos que você está tentando excluir só estão disponíveis no seu dispositivo e não podem ser recuperados se forem excluídos",
|
||||
@@ -1214,11 +1214,11 @@
|
||||
"addAName": "Adicione um nome",
|
||||
"findPeopleByName": "Encontre pessoas rapidamente por nome",
|
||||
"addViewers": "{count, plural, zero {Adicionar visualizador} one {Adicionar visualizador} other {Adicionar Visualizadores}}",
|
||||
"addCollaborators": "{count, plural, zero {Adicionar colaborador} one {Adicionar coloborador} other {Adicionar colaboradores}}",
|
||||
"addCollaborators": "{count, plural, zero {Adicionar colaborador} one {Adicionar colaborador} other {Adicionar colaboradores}}",
|
||||
"longPressAnEmailToVerifyEndToEndEncryption": "Pressione e segure um e-mail para verificar a criptografia de ponta a ponta.",
|
||||
"developerSettingsWarning": "Tem certeza de que deseja modificar as configurações de Desenvolvedor?",
|
||||
"developerSettings": "Configurações de desenvolvedor",
|
||||
"serverEndpoint": "Servidor endpoint",
|
||||
"serverEndpoint": "Endpoint do servidor",
|
||||
"invalidEndpoint": "Endpoint inválido",
|
||||
"invalidEndpointMessage": "Desculpe, o endpoint que você inseriu é inválido. Por favor, insira um endpoint válido e tente novamente.",
|
||||
"endpointUpdatedMessage": "Endpoint atualizado com sucesso",
|
||||
@@ -1252,17 +1252,28 @@
|
||||
"right": "Direita",
|
||||
"whatsNew": "O que há de novo",
|
||||
"reviewSuggestions": "Revisar sugestões",
|
||||
"reenterPassword": "Re-enter password",
|
||||
"reenterPin": "Re-enter PIN",
|
||||
"deviceLock": "Device lock",
|
||||
"pinLock": "PIN lock",
|
||||
"next": "Next",
|
||||
"setNewPassword": "Set new password",
|
||||
"enterPin": "Enter PIN",
|
||||
"setNewPin": "Set new PIN",
|
||||
"appLock": "App lock",
|
||||
"noSystemLockFound": "No system lock found",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "To enable app lock, please setup device passcode or screen lock in your system settings.",
|
||||
"tapToUnlock": "Tap to unlock",
|
||||
"tooManyIncorrectAttempts": "Too many incorrect attempts"
|
||||
"useAsCover": "Usar como capa",
|
||||
"notPersonLabel": "Não é {name}?",
|
||||
"@notPersonLabel": {
|
||||
"description": "Label to indicate that the person in the photo is not the person whose name is mentioned",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "{name}",
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reenterPassword": "Reinserir senha",
|
||||
"reenterPin": "Reinserir PIN",
|
||||
"deviceLock": "Bloqueio de dispositivo",
|
||||
"pinLock": "Bloqueio PIN",
|
||||
"next": "Próximo",
|
||||
"setNewPassword": "Defina nova senha",
|
||||
"enterPin": "Insira o PIN",
|
||||
"setNewPin": "Definir novo PIN",
|
||||
"appLock": "Bloqueio de app",
|
||||
"noSystemLockFound": "Nenhum bloqueio de sistema encontrado",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Para ativar o bloqueio de app, por favor ative um método de autenticação nas configurações do sistema do seu dispositivo.",
|
||||
"tapToUnlock": "Toque para desbloquear",
|
||||
"tooManyIncorrectAttempts": "Muitas tentativas incorretas"
|
||||
}
|
||||
@@ -1252,17 +1252,28 @@
|
||||
"right": "向右",
|
||||
"whatsNew": "更新日志",
|
||||
"reviewSuggestions": "查看建议",
|
||||
"reenterPassword": "Re-enter password",
|
||||
"reenterPin": "Re-enter PIN",
|
||||
"deviceLock": "Device lock",
|
||||
"pinLock": "PIN lock",
|
||||
"next": "Next",
|
||||
"setNewPassword": "Set new password",
|
||||
"enterPin": "Enter PIN",
|
||||
"setNewPin": "Set new PIN",
|
||||
"appLock": "App lock",
|
||||
"noSystemLockFound": "No system lock found",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "To enable app lock, please setup device passcode or screen lock in your system settings.",
|
||||
"tapToUnlock": "Tap to unlock",
|
||||
"tooManyIncorrectAttempts": "Too many incorrect attempts"
|
||||
"useAsCover": "用作封面",
|
||||
"notPersonLabel": "不是 {name}?",
|
||||
"@notPersonLabel": {
|
||||
"description": "Label to indicate that the person in the photo is not the person whose name is mentioned",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "{name}",
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reenterPassword": "再次输入密码",
|
||||
"reenterPin": "再次输入 PIN 码",
|
||||
"deviceLock": "设备锁",
|
||||
"pinLock": "PIN 锁定",
|
||||
"next": "下一步",
|
||||
"setNewPassword": "设置新密码",
|
||||
"enterPin": "输入 PIN 码",
|
||||
"setNewPin": "设置新 PIN 码",
|
||||
"appLock": "应用锁",
|
||||
"noSystemLockFound": "未找到系统锁",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "要启用应用锁,请在系统设置中设置设备密码或屏幕锁定。",
|
||||
"tapToUnlock": "点击解锁",
|
||||
"tooManyIncorrectAttempts": "错误尝试次数过多"
|
||||
}
|
||||
@@ -21,7 +21,11 @@ class LocalAuthenticationService {
|
||||
) async {
|
||||
if (await _isLocalAuthSupportedOnDevice()) {
|
||||
AppLock.of(context)!.setEnabled(false);
|
||||
final result = await requestAuthentication(context, infoMessage);
|
||||
final result = await requestAuthentication(
|
||||
context,
|
||||
infoMessage,
|
||||
isAuthenticatingForInAppChange: true,
|
||||
);
|
||||
AppLock.of(context)!.setEnabled(
|
||||
await Configuration.instance.shouldShowLockScreen(),
|
||||
);
|
||||
@@ -39,15 +43,17 @@ class LocalAuthenticationService {
|
||||
BuildContext context,
|
||||
String? savedPin,
|
||||
String? savedPassword, {
|
||||
bool isOnOpeningApp = false,
|
||||
bool isAuthenticatingOnAppLaunch = false,
|
||||
bool isAuthenticatingForInAppChange = false,
|
||||
}) async {
|
||||
if (savedPassword != null) {
|
||||
final result = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return LockScreenPassword(
|
||||
isAuthenticating: true,
|
||||
isOnOpeningApp: isOnOpeningApp,
|
||||
isChangingLockScreenSettings: true,
|
||||
isAuthenticatingForInAppChange: isAuthenticatingForInAppChange,
|
||||
isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch,
|
||||
authPass: savedPassword,
|
||||
);
|
||||
},
|
||||
@@ -62,8 +68,9 @@ class LocalAuthenticationService {
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return LockScreenPin(
|
||||
isAuthenticating: true,
|
||||
isOnOpeningApp: isOnOpeningApp,
|
||||
isChangingLockScreenSettings: true,
|
||||
isAuthenticatingForInAppChange: isAuthenticatingForInAppChange,
|
||||
isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch,
|
||||
authPin: savedPin,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ class UpdateService {
|
||||
static final UpdateService instance = UpdateService._privateConstructor();
|
||||
static const kUpdateAvailableShownTimeKey = "update_available_shown_time_key";
|
||||
static const changeLogVersionKey = "update_change_log_key";
|
||||
static const currentChangeLogVersion = 20;
|
||||
static const currentChangeLogVersion = 21;
|
||||
|
||||
LatestVersionInfo? _latestVersion;
|
||||
final _logger = Logger("UpdateService");
|
||||
|
||||
@@ -18,11 +18,6 @@ class ChangeLogPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChangeLogPageState extends State<ChangeLogPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final enteColorScheme = getEnteColorScheme(context);
|
||||
@@ -119,20 +114,16 @@ class _ChangeLogPageState extends State<ChangeLogPage> {
|
||||
final List<ChangeLogEntry> items = [];
|
||||
items.addAll([
|
||||
ChangeLogEntry(
|
||||
"Send links ✨",
|
||||
'Introducing a beautiful way to share photos in original quality, end-to-end encrypted. Select your photos and click on "Send link" to see the magic!',
|
||||
"Custom App Lock ✨",
|
||||
'Now choose from PIN, password or the default system lock to lock the app. You can set this up in Settings > Security > App lock.',
|
||||
),
|
||||
ChangeLogEntry(
|
||||
"Video editor",
|
||||
"Crop, clip and flip your videos, with Ente's in-built video editor. The editor works fully offline and will help with all your basic editing tasks.",
|
||||
"Select All ✨",
|
||||
"Selecting all files from gallery made easy with just one click! Select any item from gallery to see the option.",
|
||||
),
|
||||
ChangeLogEntry(
|
||||
"Passkeys",
|
||||
"Now secure your Ente account with passkeys or hardware keys. You can add your keys within Settings > Security > Passkeys.",
|
||||
),
|
||||
ChangeLogEntry(
|
||||
"View large files",
|
||||
"Find those items that take up the most amount of storage, and easily declutter your library. Open Settings > Backup > Free up space to learn more.",
|
||||
"Bug Fixes",
|
||||
"Many a bugs were squashed in this release. If you run into any bugs, please write to team@ente.io, or let us know on Discord! 🙏",
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ class _LockScreenConfirmPasswordState extends State<LockScreenConfirmPassword> {
|
||||
height: 75,
|
||||
width: 75,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: colorTheme.fillFaintPressed,
|
||||
color: colorTheme.fillFaintPressed,
|
||||
value: 1,
|
||||
strokeWidth: 1.5,
|
||||
),
|
||||
|
||||
@@ -16,14 +16,24 @@ import "package:photos/utils/lock_screen_settings.dart";
|
||||
class LockScreenPassword extends StatefulWidget {
|
||||
const LockScreenPassword({
|
||||
super.key,
|
||||
this.isAuthenticating = false,
|
||||
this.isOnOpeningApp = false,
|
||||
this.isChangingLockScreenSettings = false,
|
||||
this.isAuthenticatingOnAppLaunch = false,
|
||||
this.isAuthenticatingForInAppChange = false,
|
||||
this.authPass,
|
||||
});
|
||||
|
||||
//Is false when setting a new password
|
||||
final bool isAuthenticating;
|
||||
final bool isOnOpeningApp;
|
||||
/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings.
|
||||
/// Set to true when the app requires the user to authenticate before allowing
|
||||
/// changes to the lock screen settings.
|
||||
final bool isChangingLockScreenSettings;
|
||||
|
||||
/// [isAuthenticatingOnAppLaunch] Authentication required on app launch.
|
||||
/// Set to true when the app requires the user to authenticate immediately upon opening.
|
||||
final bool isAuthenticatingOnAppLaunch;
|
||||
|
||||
/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password).
|
||||
/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes.
|
||||
final bool isAuthenticatingForInAppChange;
|
||||
final String? authPass;
|
||||
@override
|
||||
State<LockScreenPassword> createState() => _LockScreenPasswordState();
|
||||
@@ -140,7 +150,7 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
height: 75,
|
||||
width: 75,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: colorTheme.fillFaintPressed,
|
||||
color: colorTheme.fillFaintPressed,
|
||||
value: 1,
|
||||
strokeWidth: 1.5,
|
||||
),
|
||||
@@ -155,7 +165,7 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.isAuthenticating
|
||||
widget.isChangingLockScreenSettings
|
||||
? S.of(context).enterPassword
|
||||
: S.of(context).setNewPassword,
|
||||
textAlign: TextAlign.center,
|
||||
@@ -201,7 +211,8 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
if (widget.authPass == base64Encode(hash)) {
|
||||
await _lockscreenSetting.setInvalidAttemptCount(0);
|
||||
|
||||
widget.isOnOpeningApp
|
||||
widget.isAuthenticatingOnAppLaunch ||
|
||||
widget.isAuthenticatingForInAppChange
|
||||
? Navigator.of(context).pop(true)
|
||||
: Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
@@ -210,10 +221,10 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
if (widget.isOnOpeningApp) {
|
||||
if (widget.isAuthenticatingOnAppLaunch) {
|
||||
invalidAttemptsCount++;
|
||||
await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount);
|
||||
if (invalidAttemptsCount > 4) {
|
||||
await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount);
|
||||
Navigator.of(context).pop(false);
|
||||
}
|
||||
}
|
||||
@@ -224,7 +235,7 @@ class _LockScreenPasswordState extends State<LockScreenPassword> {
|
||||
}
|
||||
|
||||
Future<void> _confirmPassword() async {
|
||||
if (widget.isAuthenticating) {
|
||||
if (widget.isChangingLockScreenSettings) {
|
||||
await _confirmPasswordAuth(_passwordController.text);
|
||||
return;
|
||||
} else {
|
||||
|
||||
@@ -18,14 +18,24 @@ import 'package:pinput/pinput.dart';
|
||||
class LockScreenPin extends StatefulWidget {
|
||||
const LockScreenPin({
|
||||
super.key,
|
||||
this.isAuthenticating = false,
|
||||
this.isOnOpeningApp = false,
|
||||
this.isChangingLockScreenSettings = false,
|
||||
this.isAuthenticatingOnAppLaunch = false,
|
||||
this.isAuthenticatingForInAppChange = false,
|
||||
this.authPin,
|
||||
});
|
||||
|
||||
//Is false when setting a new password
|
||||
final bool isAuthenticating;
|
||||
final bool isOnOpeningApp;
|
||||
/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings.
|
||||
/// Set to true when the app requires the user to authenticate before allowing
|
||||
/// changes to the lock screen settings.
|
||||
final bool isChangingLockScreenSettings;
|
||||
|
||||
/// [isAuthenticatingOnAppLaunch] Authentication required on app launch.
|
||||
/// Set to true when the app requires the user to authenticate immediately upon opening.
|
||||
final bool isAuthenticatingOnAppLaunch;
|
||||
|
||||
/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password).
|
||||
/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes.
|
||||
final bool isAuthenticatingForInAppChange;
|
||||
final String? authPin;
|
||||
@override
|
||||
State<LockScreenPin> createState() => _LockScreenPinState();
|
||||
@@ -62,7 +72,8 @@ class _LockScreenPinState extends State<LockScreenPin> {
|
||||
if (widget.authPin == base64Encode(hash)) {
|
||||
invalidAttemptsCount = 0;
|
||||
await _lockscreenSetting.setInvalidAttemptCount(0);
|
||||
widget.isOnOpeningApp
|
||||
widget.isAuthenticatingOnAppLaunch ||
|
||||
widget.isAuthenticatingForInAppChange
|
||||
? Navigator.of(context).pop(true)
|
||||
: Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
@@ -81,10 +92,10 @@ class _LockScreenPinState extends State<LockScreenPin> {
|
||||
isPinValid = false;
|
||||
});
|
||||
|
||||
if (widget.isOnOpeningApp) {
|
||||
if (widget.isAuthenticatingOnAppLaunch) {
|
||||
invalidAttemptsCount++;
|
||||
await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount);
|
||||
if (invalidAttemptsCount > 4) {
|
||||
await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount);
|
||||
Navigator.of(context).pop(false);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +104,7 @@ class _LockScreenPinState extends State<LockScreenPin> {
|
||||
}
|
||||
|
||||
Future<void> _confirmPin(String code) async {
|
||||
if (widget.isAuthenticating) {
|
||||
if (widget.isChangingLockScreenSettings) {
|
||||
await confirmPinAuth(code);
|
||||
return;
|
||||
} else {
|
||||
@@ -220,7 +231,7 @@ class _LockScreenPinState extends State<LockScreenPin> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.isAuthenticating
|
||||
widget.isChangingLockScreenSettings
|
||||
? S.of(context).enterPin
|
||||
: S.of(context).setNewPin,
|
||||
style: textTheme.bodyBold,
|
||||
|
||||
@@ -7,9 +7,9 @@ import "package:flutter/scheduler.dart";
|
||||
import "package:flutter_animate/flutter_animate.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:photos/core/configuration.dart";
|
||||
import "package:photos/ente_theme_data.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/buttons/icon_button_widget.dart";
|
||||
import 'package:photos/ui/tools/app_lock.dart';
|
||||
@@ -35,16 +35,7 @@ class _LockScreenState extends State<LockScreen>
|
||||
int lockedTimeInSeconds = 0;
|
||||
int invalidAttemptCount = 0;
|
||||
int remainingTimeInSeconds = 0;
|
||||
bool showErrorMessage = true;
|
||||
final _lockscreenSetting = LockScreenSettings.instance;
|
||||
late final AnimationController _controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
vsync: this,
|
||||
);
|
||||
late final animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
late Brightness _platformBrightness;
|
||||
|
||||
@override
|
||||
@@ -70,6 +61,16 @@ class _LockScreenState extends State<LockScreen>
|
||||
final colorTheme = getEnteColorScheme(context);
|
||||
final textTheme = getEnteTextTheme(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.logout_outlined),
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
onPressed: () {
|
||||
_onLogoutTapped(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
body: GestureDetector(
|
||||
onTap: () {
|
||||
isTimerRunning ? null : _showLockScreen(source: "tap");
|
||||
@@ -125,8 +126,10 @@ class _LockScreenState extends State<LockScreen>
|
||||
width: 75,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(
|
||||
begin: 0,
|
||||
end: _getFractionOfTimeElapsed(),
|
||||
begin: isTimerRunning ? 0 : 1,
|
||||
end: isTimerRunning
|
||||
? _getFractionOfTimeElapsed()
|
||||
: 1,
|
||||
),
|
||||
duration: const Duration(seconds: 1),
|
||||
builder: (context, value, _) =>
|
||||
@@ -202,39 +205,27 @@ class _LockScreenState extends State<LockScreen>
|
||||
return shortestSide > 600 ? true : false;
|
||||
}
|
||||
|
||||
Future<void> _autoLogoutOnMaxInvalidAttempts() async {
|
||||
final AlertDialog alert = AlertDialog(
|
||||
title: Text(S.of(context).tooManyIncorrectAttempts),
|
||||
content: Text(S.of(context).pleaseLoginAgain),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
S.of(context).ok,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.greenAlternative,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
final dialog =
|
||||
createProgressDialog(context, S.of(context).loggingOut);
|
||||
await dialog.show();
|
||||
await Configuration.instance.logout();
|
||||
await dialog.hide();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
void _onLogoutTapped(BuildContext context) {
|
||||
showChoiceActionSheet(
|
||||
context,
|
||||
title: S.of(context).areYouSureYouWantToLogout,
|
||||
firstButtonLabel: S.of(context).yesLogout,
|
||||
isCritical: true,
|
||||
firstButtonOnTap: () async {
|
||||
await UserService.instance.logout(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _autoLogoutOnMaxInvalidAttempts() async {
|
||||
_logger.info("Auto logout on max invalid attempts");
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
final dialog = createProgressDialog(context, S.of(context).loggingOut);
|
||||
await dialog.show();
|
||||
await Configuration.instance.logout();
|
||||
await dialog.hide();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
_logger.info(state.toString());
|
||||
|
||||
@@ -3,7 +3,6 @@ import "package:photos/face/model/person.dart";
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import 'package:photos/models/gallery_type.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/theme/effects.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
|
||||
@@ -86,15 +85,13 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
flagService.internalUser
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: SelectAllButton(
|
||||
backgroundColor: widget.backgroundColor,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
if (flagService.internalUser) const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: SelectAllButton(
|
||||
backgroundColor: widget.backgroundColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: shadowFloatFaintLight,
|
||||
|
||||
@@ -11,6 +11,7 @@ Future<bool> requestAuthentication(
|
||||
BuildContext context,
|
||||
String reason, {
|
||||
bool isOpeningApp = false,
|
||||
bool isAuthenticatingForInAppChange = false,
|
||||
}) async {
|
||||
Logger("AuthUtil").info("Requesting authentication");
|
||||
await LocalAuthentication().stopAuthentication();
|
||||
@@ -23,7 +24,8 @@ Future<bool> requestAuthentication(
|
||||
context,
|
||||
savedPin,
|
||||
savedPassword,
|
||||
isOnOpeningApp: isOpeningApp,
|
||||
isAuthenticatingOnAppLaunch: isOpeningApp,
|
||||
isAuthenticatingForInAppChange: isAuthenticatingForInAppChange,
|
||||
);
|
||||
} else {
|
||||
return await LocalAuthentication().authenticate(
|
||||
|
||||
@@ -12,7 +12,7 @@ description: ente photos application
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
|
||||
version: 0.9.4+904
|
||||
version: 0.9.7+907
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
||||
@@ -18,6 +18,9 @@ server {
|
||||
|
||||
server_name api.ente.io;
|
||||
|
||||
# Allow HTTP request body up to 4 MB (default is 1 MB).
|
||||
client_max_body_size 4m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://museum;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
@@ -33,8 +33,10 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
useEffect(() => {
|
||||
disableDiskLogs();
|
||||
// The accounts app has no local state, but some older builds might've
|
||||
// leftover some scraps. Clear it out. This code added 1 July 2024, can
|
||||
// be removed after a while (tag: Migration).
|
||||
// leftover some scraps. Clear it out.
|
||||
//
|
||||
// This code added on 1 July 2024, can be removed soon since this data
|
||||
// was never saved before this was released (tag: Migration).
|
||||
clearData();
|
||||
void setupI18n().finally(() => setIsI18nReady(true));
|
||||
logUnhandledErrorsAndRejections(true);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import log from "@/next/log";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { CenteredFlex } from "@ente/shared/components/Container";
|
||||
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
|
||||
import EnteButton from "@ente/shared/components/EnteButton";
|
||||
import { EnteDrawer } from "@ente/shared/components/EnteDrawer";
|
||||
import FormPaper from "@ente/shared/components/Form/FormPaper";
|
||||
import InfoItem from "@ente/shared/components/Info/InfoItem";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import MenuItemDivider from "@ente/shared/components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "@ente/shared/components/Menu/MenuItemGroup";
|
||||
import SingleInputForm from "@ente/shared/components/SingleInputForm";
|
||||
import Titlebar from "@ente/shared/components/Titlebar";
|
||||
import { formatDateTimeFull } from "@ente/shared/time/format";
|
||||
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clientPackageName } from "@/next/app";
|
||||
import { isDevBuild } from "@/next/env";
|
||||
import { clientPackageHeader } from "@/next/http";
|
||||
import { clientPackageHeader, ensureOk, HTTPError } from "@/next/http";
|
||||
import { apiURL } from "@/next/origins";
|
||||
import { TwoFactorAuthorizationResponse } from "@/next/types/credentials";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
@@ -57,11 +57,10 @@ const GetPasskeysResponse = z.object({
|
||||
* has no passkeys.
|
||||
*/
|
||||
export const getPasskeys = async (token: string) => {
|
||||
const url = await apiURL("/passkeys");
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(await apiURL("/passkeys"), {
|
||||
headers: accountsAuthenticatedRequestHeaders(token),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
const { passkeys } = GetPasskeysResponse.parse(await res.json());
|
||||
return passkeys ?? [];
|
||||
};
|
||||
@@ -86,7 +85,7 @@ export const renamePasskey = async (
|
||||
method: "PATCH",
|
||||
headers: accountsAuthenticatedRequestHeaders(token),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -97,12 +96,11 @@ export const renamePasskey = async (
|
||||
* @param id The `id` of the existing passkey to delete.
|
||||
*/
|
||||
export const deletePasskey = async (token: string, id: string) => {
|
||||
const url = await apiURL(`/passkeys/${id}`);
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(await apiURL(`/passkeys/${id}`), {
|
||||
method: "DELETE",
|
||||
headers: accountsAuthenticatedRequestHeaders(token),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -148,12 +146,11 @@ interface BeginPasskeyRegistrationResponse {
|
||||
}
|
||||
|
||||
const beginPasskeyRegistration = async (token: string) => {
|
||||
const url = await apiURL("/passkeys/registration/begin");
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(await apiURL("/passkeys/registration/begin"), {
|
||||
method: "POST",
|
||||
headers: accountsAuthenticatedRequestHeaders(token),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
|
||||
// [Note: Converting binary data in WebAuthn API payloads]
|
||||
//
|
||||
@@ -310,7 +307,7 @@ const finishPasskeyRegistration = async ({
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -422,7 +419,7 @@ export const beginPasskeyAuthentication = async (
|
||||
if (!res.ok) {
|
||||
if (res.status == 409)
|
||||
throw new Error(passkeySessionAlreadyClaimedErrorMessage);
|
||||
throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
throw new HTTPError(res);
|
||||
}
|
||||
|
||||
// See: [Note: Converting binary data in WebAuthn API payloads]
|
||||
@@ -527,7 +524,7 @@ export const finishPasskeyAuthentication = async ({
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${url}: HTTP ${res.status}`);
|
||||
ensureOk(res);
|
||||
|
||||
return TwoFactorAuthorizationResponse.parse(await res.json());
|
||||
};
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
"@ente/eslint-config": "*",
|
||||
"@ente/shared": "*",
|
||||
"jssha": "~3.3.1",
|
||||
"otpauth": "^9"
|
||||
"otpauth": "9.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ export const getAuthCodes = async (): Promise<Code[]> => {
|
||||
}
|
||||
}),
|
||||
);
|
||||
// Remove undefined values
|
||||
const filteredAuthCodes = authCodes.filter((f): f is Code => !!f);
|
||||
const filteredAuthCodes = authCodes.filter((f) => f !== undefined);
|
||||
filteredAuthCodes.sort((a, b) => {
|
||||
if (a.issuer && b.issuer) {
|
||||
return a.issuer.localeCompare(b.issuer);
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
"@ente/shared": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chromecast-caf-receiver": "^6.0.14"
|
||||
"@types/chromecast-caf-receiver": "^6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { FILE_TYPE } from "@/media/file-type";
|
||||
import { isHEICExtension, isNonWebImageFileExtension } from "@/media/formats";
|
||||
import { isHEICExtension, needsJPEGConversion } from "@/media/formats";
|
||||
import { heicToJPEG } from "@/media/heic-convert";
|
||||
import { decodeLivePhoto } from "@/media/live-photo";
|
||||
import type {
|
||||
@@ -258,8 +258,8 @@ const isFileEligible = (file: EnteFile) => {
|
||||
// extension. To detect the actual type, we need to sniff the MIME type, but
|
||||
// that requires downloading and decrypting the file first.
|
||||
const [, extension] = nameAndExtension(file.metadata.title);
|
||||
if (extension && isNonWebImageFileExtension(extension)) {
|
||||
// Of the known non-web types, we support HEIC.
|
||||
if (extension && needsJPEGConversion(extension)) {
|
||||
// On the web, we only support HEIC conversion.
|
||||
return isHEICExtension(extension);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"@/build-config": "*",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@vitejs/plugin-react": "^4.2",
|
||||
"vite": "^5.2"
|
||||
"@vitejs/plugin-react": "^4.3",
|
||||
"vite": "^5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Container } from "./components/Container";
|
||||
import { parseAndHandleRequest } from "./services/billing-service";
|
||||
import { parseAndHandleRequest } from "./services/billing";
|
||||
import S from "./utils/strings";
|
||||
|
||||
export const App: React.FC = () => {
|
||||
@@ -18,4 +17,8 @@ export const App: React.FC = () => {
|
||||
return <Container>{failed ? S.error_generic : <Spinner />}</Container>;
|
||||
};
|
||||
|
||||
const Container: React.FC<React.PropsWithChildren> = ({ children }) => (
|
||||
<div className="container">{children}</div>
|
||||
);
|
||||
|
||||
const Spinner: React.FC = () => <div className="loading-spinner"></div>;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export const Container: React.FC<React.PropsWithChildren> = ({ children }) => (
|
||||
<div className="container">{children}</div>
|
||||
);
|
||||
@@ -28,7 +28,6 @@
|
||||
"memoize-one": "^6.0.0",
|
||||
"ml-matrix": "^6.11",
|
||||
"p-debounce": "^4.0.0",
|
||||
"p-queue": "^7.1.0",
|
||||
"photoswipe": "file:./thirdparty/photoswipe",
|
||||
"piexifjs": "^1.0.6",
|
||||
"pure-react-carousel": "^1.30.1",
|
||||
@@ -36,7 +35,7 @@
|
||||
"react-otp-input": "^2.3.1",
|
||||
"react-select": "^4.3.1",
|
||||
"react-top-loading-bar": "^2.0.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-virtualized-auto-sizer": "^1.0",
|
||||
"react-window": "^1.8.6",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"similarity-transformation": "^0.0.1",
|
||||
@@ -49,10 +48,10 @@
|
||||
"@ente/eslint-config": "*",
|
||||
"@next/bundle-analyzer": "^14.1",
|
||||
"@types/bs58": "^4.0.1",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"@types/leaflet": "^1.9",
|
||||
"@types/photoswipe": "^4.1.1",
|
||||
"@types/react-select": "^4.0.15",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/react-window-infinite-loader": "^1.0.3",
|
||||
"@types/uuid": "^9.0.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { DialogProps, Stack } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import { COLLECTION_ROLE, Collection } from "types/collection";
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { FlexWrapper } from "@ente/shared/components/Container";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import SubmitButton from "@ente/shared/components/SubmitButton";
|
||||
import DoneIcon from "@mui/icons-material/Done";
|
||||
import { Button, FormHelperText, Stack } from "@mui/material";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import Avatar from "components/pages/gallery/Avatar";
|
||||
import { Formik, type FormikHelpers } from "formik";
|
||||
import { t } from "i18next";
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import Add from "@mui/icons-material/Add";
|
||||
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
|
||||
@@ -5,11 +12,6 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import ModeEditIcon from "@mui/icons-material/ModeEdit";
|
||||
import Photo from "@mui/icons-material/Photo";
|
||||
import { DialogProps, Stack } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import Avatar from "components/pages/gallery/Avatar";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import log from "@/next/log";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
@@ -5,10 +8,6 @@ import DoneIcon from "@mui/icons-material/Done";
|
||||
import ModeEditIcon from "@mui/icons-material/ModeEdit";
|
||||
import PhotoIcon from "@mui/icons-material/Photo";
|
||||
import { DialogProps, Stack, Typography } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { COLLECTION_ROLE, Collection } from "types/collection";
|
||||
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import Workspaces from "@mui/icons-material/Workspaces";
|
||||
import { Stack } from "@mui/material";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import AvatarGroup from "components/pages/gallery/AvatarGroup";
|
||||
import { t } from "i18next";
|
||||
import AddParticipant from "./AddParticipant";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { DialogProps, Stack } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { CollectionSummaryType } from "constants/collection";
|
||||
import { t } from "i18next";
|
||||
import { Collection, CollectionSummary } from "types/collection";
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import DownloadSharp from "@mui/icons-material/DownloadSharp";
|
||||
import LinkIcon from "@mui/icons-material/Link";
|
||||
import PublicIcon from "@mui/icons-material/Public";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { t } from "i18next";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import { DialogProps, Stack } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Collection, PublicURL, UpdatePublicURL } from "types/collection";
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import RemoveCircleOutline from "@mui/icons-material/RemoveCircleOutline";
|
||||
import { DialogProps, Stack, Typography } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import { formatDateTime } from "@ente/shared/time/format";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import { DialogProps, Stack } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Collection, PublicURL, UpdatePublicURL } from "types/collection";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MenuItemGroup, MenuSectionTitle } from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import { Stack } from "@mui/material";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { t } from "i18next";
|
||||
import { Collection, PublicURL, UpdatePublicURL } from "types/collection";
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MenuItemDivider, MenuItemGroup } from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopyOutlined";
|
||||
@@ -5,8 +6,6 @@ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||
import LinkIcon from "@mui/icons-material/Link";
|
||||
import PublicIcon from "@mui/icons-material/Public";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import { t } from "i18next";
|
||||
import { useState } from "react";
|
||||
import { Collection, PublicURL } from "types/collection";
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
|
||||
import ModeEditIcon from "@mui/icons-material/ModeEdit";
|
||||
import Photo from "@mui/icons-material/Photo";
|
||||
import { Stack } from "@mui/material";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import Avatar from "components/pages/gallery/Avatar";
|
||||
import { CollectionSummaryType } from "constants/collection";
|
||||
import { t } from "i18next";
|
||||
|
||||
@@ -195,7 +195,7 @@ function ExportDirectory({ exportFolder, changeExportDirectory, exportStage }) {
|
||||
<>
|
||||
{!exportFolder ? (
|
||||
<Button color={"accent"} onClick={changeExportDirectory}>
|
||||
{t("SELECT_FOLDER")}
|
||||
{t("select_folder")}
|
||||
</Button>
|
||||
) : (
|
||||
<VerticallyCenteredFlex>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Divider } from "@mui/material";
|
||||
interface Iprops {
|
||||
hasIcon?: boolean;
|
||||
}
|
||||
export default function MenuItemDivider({ hasIcon = false }: Iprops) {
|
||||
return (
|
||||
<Divider
|
||||
sx={{
|
||||
"&&&": {
|
||||
my: 0,
|
||||
ml: hasIcon ? "48px" : "16px",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { styled } from "@mui/material";
|
||||
|
||||
export const MenuItemGroup = styled("div")(
|
||||
({ theme }) => `
|
||||
& > .MuiMenuItem-root{
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
& > .MuiMenuItem-root:not(:last-of-type) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
& > .MuiMenuItem-root:not(:first-of-type) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
background-color: ${theme.colors.fill.faint};
|
||||
border-radius: 8px;
|
||||
`,
|
||||
);
|
||||
@@ -1,28 +0,0 @@
|
||||
import { VerticallyCenteredFlex } from "@ente/shared/components/Container";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface Iprops {
|
||||
title: string;
|
||||
icon?: JSX.Element;
|
||||
}
|
||||
|
||||
export default function MenuSectionTitle({ title, icon }: Iprops) {
|
||||
return (
|
||||
<VerticallyCenteredFlex
|
||||
px="8px"
|
||||
py={"6px"}
|
||||
gap={"8px"}
|
||||
sx={{
|
||||
"& > svg": {
|
||||
fontSize: "17px",
|
||||
color: (theme) => theme.colors.stroke.muted,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{icon && icon}
|
||||
<Typography variant="small" color="text.muted">
|
||||
{title}
|
||||
</Typography>
|
||||
</VerticallyCenteredFlex>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import CopyButton from "@ente/shared/components/CodeBlock/CopyButton";
|
||||
import { formatDateTimeFull } from "@ente/shared/time/format";
|
||||
import { Box, Stack, styled, Typography } from "@mui/material";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { t } from "i18next";
|
||||
import React from "react";
|
||||
import { FileInfoSidebar } from ".";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { UnidentifiedFaces } from "@/new/photos/components/PeopleList";
|
||||
import { isMLEnabled } from "@/new/photos/services/ml";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import CopyButton from "@ente/shared/components/CodeBlock/CopyButton";
|
||||
import { FlexWrapper } from "@ente/shared/components/Container";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
@@ -11,9 +14,6 @@ import LocationOnOutlined from "@mui/icons-material/LocationOnOutlined";
|
||||
import TextSnippetOutlined from "@mui/icons-material/TextSnippetOutlined";
|
||||
import { Box, DialogProps, Link, Stack, styled } from "@mui/material";
|
||||
import { Chip } from "components/Chip";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { UnidentifiedFaces } from "components/ml/PeopleList";
|
||||
import LinkButton from "components/pages/gallery/LinkButton";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MenuItemGroup, MenuSectionTitle } from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import { Box, Slider } from "@mui/material";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { t } from "i18next";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MenuItemGroup, MenuSectionTitle } from "@/new/shared/components/Menu";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import CropIcon from "@mui/icons-material/Crop";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { t } from "i18next";
|
||||
import type { MutableRefObject } from "react";
|
||||
import { useContext } from "react";
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import log from "@/next/log";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import Crop169Icon from "@mui/icons-material/Crop169";
|
||||
@@ -6,9 +11,6 @@ import CropSquareIcon from "@mui/icons-material/CropSquare";
|
||||
import FlipIcon from "@mui/icons-material/Flip";
|
||||
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
|
||||
import RotateRightIcon from "@mui/icons-material/RotateRight";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { t } from "i18next";
|
||||
import { Fragment, useContext } from "react";
|
||||
import { ImageEditorOverlayContext } from ".";
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import downloadManager from "@/new/photos/services/download";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import {
|
||||
MenuItemDivider,
|
||||
MenuItemGroup,
|
||||
MenuSectionTitle,
|
||||
} from "@/new/shared/components/Menu";
|
||||
import { nameAndExtension } from "@/next/file";
|
||||
import log from "@/next/log";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
@@ -26,10 +32,6 @@ import {
|
||||
Tabs,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import MenuItemDivider from "components/Menu/MenuItemDivider";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import { CORNER_THRESHOLD, FILTER_DEFAULT_VALUES } from "constants/photoEditor";
|
||||
import { t } from "i18next";
|
||||
import { AppContext } from "pages/_app";
|
||||
@@ -775,7 +777,7 @@ const canvasToFile = async (
|
||||
const [originalName] = nameAndExtension(originalFileName);
|
||||
const fileName = `${originalName}-edited.${extension}`;
|
||||
|
||||
log.debug(() => ({ a: "canvas => file", blob, type: blob.type, mimeType }));
|
||||
log.debug(() => ["canvas => file", { blob, type: blob.type, mimeType }]);
|
||||
|
||||
return new File([blob], fileName);
|
||||
};
|
||||
|
||||
@@ -14,11 +14,11 @@ import {
|
||||
} from "utils/file";
|
||||
|
||||
import { FILE_TYPE } from "@/media/file-type";
|
||||
import { isNonWebImageFileExtension } from "@/media/formats";
|
||||
import { isHEICExtension, needsJPEGConversion } from "@/media/formats";
|
||||
import downloadManager from "@/new/photos/services/download";
|
||||
import type { LoadedLivePhotoSourceURL } from "@/new/photos/types/file";
|
||||
import { detectFileTypeInfo } from "@/new/photos/utils/detect-type";
|
||||
import { isNativeConvertibleToJPEG } from "@/new/photos/utils/file";
|
||||
import { isDesktop } from "@/next/app";
|
||||
import { lowercaseExtension } from "@/next/file";
|
||||
import { FlexWrapper } from "@ente/shared/components/Container";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
@@ -350,11 +350,16 @@ function PhotoViewer(props: Iprops) {
|
||||
|
||||
function updateShowEditButton(file: EnteFile) {
|
||||
const extension = lowercaseExtension(file.metadata.title);
|
||||
const isSupported =
|
||||
!isNonWebImageFileExtension(extension) ||
|
||||
// TODO: This condition doesn't sound correct when running in the
|
||||
// web app?
|
||||
isNativeConvertibleToJPEG(extension);
|
||||
// Assume it is supported.
|
||||
let isSupported = true;
|
||||
if (needsJPEGConversion(extension)) {
|
||||
// See if the file is on the whitelist of extensions that we know
|
||||
// will not be directly renderable.
|
||||
if (!isDesktop) {
|
||||
// On the web, we only support HEIC conversion.
|
||||
isSupported = isHEICExtension(extension);
|
||||
}
|
||||
}
|
||||
setShowEditButton(
|
||||
file.metadata.fileType === FILE_TYPE.IMAGE && isSupported,
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PeopleList } from "@/new/photos/components/PeopleList";
|
||||
import { isMLEnabled } from "@/new/photos/services/ml";
|
||||
import { Row } from "@ente/shared/components/Container";
|
||||
import { Box, styled } from "@mui/material";
|
||||
import { PeopleList } from "components/ml/PeopleList";
|
||||
import { t } from "i18next";
|
||||
import { components } from "react-select";
|
||||
import { Suggestion, SuggestionType } from "types/search";
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { VerticallyCenteredFlex } from "@ente/shared/components/Container";
|
||||
import { MLSettingsBeta } from "@/new/photos/components/MLSettingsBeta";
|
||||
import { canEnableML } from "@/new/photos/services/ml";
|
||||
import { EnteDrawer } from "@/new/shared/components/EnteDrawer";
|
||||
import { MenuItemGroup, MenuSectionTitle } from "@/new/shared/components/Menu";
|
||||
import { Titlebar } from "@/new/shared/components/Titlebar";
|
||||
import { isDesktop } from "@/next/app";
|
||||
import { pt } from "@/next/i18n";
|
||||
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import ScienceIcon from "@mui/icons-material/Science";
|
||||
import { Box, DialogProps, Stack, Typography } from "@mui/material";
|
||||
import { EnteDrawer } from "components/EnteDrawer";
|
||||
import { MenuItemGroup } from "components/Menu/MenuItemGroup";
|
||||
import MenuSectionTitle from "components/Menu/MenuSectionTitle";
|
||||
import Titlebar from "components/Titlebar";
|
||||
import { MLSearchSettings } from "components/ml/MLSearchSettings";
|
||||
import { Box, DialogProps, Stack } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import isElectron from "is-electron";
|
||||
import { AppContext } from "pages/_app";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { CLIPIndexingStatus, clipService } from "services/clip-service";
|
||||
import { formatNumber } from "utils/number/format";
|
||||
|
||||
export default function AdvancedSettings({ open, onClose, onRootClose }) {
|
||||
const appContext = useContext(AppContext);
|
||||
const [mlSearchSettingsView, setMlSearchSettingsView] = useState(false);
|
||||
|
||||
const openMlSearchSettings = () => setMlSearchSettingsView(true);
|
||||
const closeMlSearchSettings = () => setMlSearchSettingsView(false);
|
||||
const [showMLSettings, setShowMLSettings] = useState(false);
|
||||
const [openMLSettings, setOpenMLSettings] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDesktop) void canEnableML().then(setShowMLSettings);
|
||||
}, []);
|
||||
const handleRootClose = () => {
|
||||
onClose();
|
||||
onRootClose();
|
||||
@@ -39,17 +39,6 @@ export default function AdvancedSettings({ open, onClose, onRootClose }) {
|
||||
appContext.setIsCFProxyDisabled(!appContext.isCFProxyDisabled);
|
||||
};
|
||||
|
||||
const [indexingStatus, setIndexingStatus] = useState<CLIPIndexingStatus>({
|
||||
indexed: 0,
|
||||
pending: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
clipService.setOnUpdateHandler(setIndexingStatus);
|
||||
clipService.getIndexingStatus().then((st) => setIndexingStatus(st));
|
||||
return () => clipService.setOnUpdateHandler(undefined);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EnteDrawer
|
||||
transitionDuration={0}
|
||||
@@ -68,21 +57,6 @@ export default function AdvancedSettings({ open, onClose, onRootClose }) {
|
||||
|
||||
<Box px={"8px"}>
|
||||
<Stack py="20px" spacing="24px">
|
||||
{isElectron() && (
|
||||
<Box>
|
||||
<MenuSectionTitle
|
||||
title={t("LABS")}
|
||||
icon={<ScienceIcon />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
<EnteMenuItem
|
||||
endIcon={<ChevronRight />}
|
||||
onClick={openMlSearchSettings}
|
||||
label={t("ML_SEARCH")}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
</Box>
|
||||
)}
|
||||
<Box>
|
||||
<MenuItemGroup>
|
||||
<EnteMenuItem
|
||||
@@ -96,48 +70,29 @@ export default function AdvancedSettings({ open, onClose, onRootClose }) {
|
||||
title={t("FASTER_UPLOAD_DESCRIPTION")}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{isElectron() && (
|
||||
<Box>
|
||||
<MenuSectionTitle
|
||||
title={t("MAGIC_SEARCH_STATUS")}
|
||||
/>
|
||||
<Stack py={"12px"} px={"12px"} spacing={"24px"}>
|
||||
<VerticallyCenteredFlex
|
||||
justifyContent="space-between"
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Typography>
|
||||
{t("INDEXED_ITEMS")}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{formatNumber(
|
||||
indexingStatus.indexed,
|
||||
)}
|
||||
</Typography>
|
||||
</VerticallyCenteredFlex>
|
||||
<VerticallyCenteredFlex
|
||||
justifyContent="space-between"
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Typography>
|
||||
{t("PENDING_ITEMS")}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{formatNumber(
|
||||
indexingStatus.pending,
|
||||
)}
|
||||
</Typography>
|
||||
</VerticallyCenteredFlex>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{showMLSettings && (
|
||||
<Box>
|
||||
<MenuSectionTitle
|
||||
title={t("LABS")}
|
||||
icon={<ScienceIcon />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
<EnteMenuItem
|
||||
endIcon={<ChevronRight />}
|
||||
onClick={() => setOpenMLSettings(true)}
|
||||
label={pt("ML search")}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
<MLSearchSettings
|
||||
open={mlSearchSettingsView}
|
||||
onClose={closeMlSearchSettings}
|
||||
|
||||
<MLSettingsBeta
|
||||
open={openMLSettings}
|
||||
onClose={() => setOpenMLSettings(false)}
|
||||
onRootClose={handleRootClose}
|
||||
/>
|
||||
</EnteDrawer>
|
||||
|
||||