code reformat

This commit is contained in:
2020-10-15 00:59:13 +03:00
parent 68a59d6093
commit 516cdf5b82
69 changed files with 635 additions and 563 deletions
+80 -99
View File
@@ -1344,38 +1344,38 @@
"dev": true "dev": true
}, },
"@fortawesome/fontawesome-common-types": { "@fortawesome/fontawesome-common-types": {
"version": "0.2.28", "version": "0.2.32",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.28.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.32.tgz",
"integrity": "sha512-gtis2/5yLdfI6n0ia0jH7NJs5i/Z/8M/ZbQL6jXQhCthEOe5Cr5NcQPhgTvFxNOtURE03/ZqUcEskdn2M+QaBg==" "integrity": "sha512-ux2EDjKMpcdHBVLi/eWZynnPxs0BtFVXJkgHIxXRl+9ZFaHPvYamAfCzeeQFqHRjuJtX90wVnMRaMQAAlctz3w=="
}, },
"@fortawesome/fontawesome-svg-core": { "@fortawesome/fontawesome-svg-core": {
"version": "1.2.28", "version": "1.2.32",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.28.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.32.tgz",
"integrity": "sha512-4LeaNHWvrneoU0i8b5RTOJHKx7E+y7jYejplR7uSVB34+mp3Veg7cbKk7NBCLiI4TyoWS1wh9ZdoyLJR8wSAdg==", "integrity": "sha512-XjqyeLCsR/c/usUpdWcOdVtWFVjPbDFBTQkn2fQRrWhhUoxriQohO2RWDxLyUM8XpD+Zzg5xwJ8gqTYGDLeGaQ==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.28" "@fortawesome/fontawesome-common-types": "^0.2.32"
} }
}, },
"@fortawesome/free-brands-svg-icons": { "@fortawesome/free-brands-svg-icons": {
"version": "5.13.0", "version": "5.15.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.0.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.1.tgz",
"integrity": "sha512-/6xXiJFCMEQxqxXbL0FPJpwq5Cv6MRrjsbJEmH/t5vOvB4dILDpnY0f7zZSlA8+TG7jwlt12miF/yZpZkykucA==", "integrity": "sha512-pkTZIWn7iuliCCgV+huDfZmZb2UjslalXGDA2PcqOVUYJmYL11y6ooFiMJkJvUZu+xgAc1gZgQe+Px12mZF0CA==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.28" "@fortawesome/fontawesome-common-types": "^0.2.32"
} }
}, },
"@fortawesome/free-solid-svg-icons": { "@fortawesome/free-solid-svg-icons": {
"version": "5.13.0", "version": "5.15.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.13.0.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.1.tgz",
"integrity": "sha512-IHUgDJdomv6YtG4p3zl1B5wWf9ffinHIvebqQOmV3U+3SLw4fC+LUCCgwfETkbTtjy5/Qws2VoVf6z/ETQpFpg==", "integrity": "sha512-EFMuKtzRMNbvjab/SvJBaOOpaqJfdSap/Nl6hst7CgrJxwfORR1drdTV6q1Ib/JVzq4xObdTDcT6sqTaXMqfdg==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.28" "@fortawesome/fontawesome-common-types": "^0.2.32"
} }
}, },
"@fortawesome/react-fontawesome": { "@fortawesome/react-fontawesome": {
"version": "0.1.10", "version": "0.1.11",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.10.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.11.tgz",
"integrity": "sha512-UGdpJiLBIqR/8xcLrCarf2pChqQFKjDTD02C7ZS/odpOVVl2YuHYRCLEOQ0GpfOk6jtYhmouSFOFoC8qNCe5cg==", "integrity": "sha512-sClfojasRifQKI0OPqTy8Ln8iIhnxR/Pv/hukBhWnBz9kQRmqi6JSH3nghlhAY7SUeIIM7B5/D2G8WjX0iepVg==",
"requires": { "requires": {
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
@@ -1775,59 +1775,59 @@
"dev": true "dev": true
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "5.12.1", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.12.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.26.0.tgz",
"integrity": "sha512-Zl7VdppUxctyaoqMSEhnDJp2rrupx8n8N2n3PSooH74yhB2Z91nt84mouczprBsw3JU1iggGyUw9seRFzDI1hw==", "integrity": "sha512-52kNVpy10Zd3gJRGFkhnOQvr80WJg7+XBqjMOE0//Akh4PfvEK3IqmAjVqysz6aHdruwTTivKF4ZoAxL/pA7Rg==",
"requires": { "requires": {
"@sentry/core": "5.12.0", "@sentry/core": "5.26.0",
"@sentry/types": "5.12.0", "@sentry/types": "5.26.0",
"@sentry/utils": "5.12.0", "@sentry/utils": "5.26.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "5.12.0", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.12.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.26.0.tgz",
"integrity": "sha512-wY4rsoX71QsGpcs9tF+OxKgDPKzIFMRvFiSRcJoPMfhFsTilQ/CBMn/c3bDtWQd9Bnr/ReQIL6NbnIjUsPHA4Q==", "integrity": "sha512-Ubrw7K52orTVsaxpz8Su40FPXugKipoQC+zPrXcH+JIMB+o18kutF81Ae4WzuUqLfP7YB91eAlRrP608zw0EXA==",
"requires": { "requires": {
"@sentry/hub": "5.12.0", "@sentry/hub": "5.26.0",
"@sentry/minimal": "5.12.0", "@sentry/minimal": "5.26.0",
"@sentry/types": "5.12.0", "@sentry/types": "5.26.0",
"@sentry/utils": "5.12.0", "@sentry/utils": "5.26.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "5.12.0", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.12.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.26.0.tgz",
"integrity": "sha512-3k7yE8BEVJsKx8mR4LcI4IN0O8pngmq44OcJ/fRUUBAPqsT38jsJdP2CaWhdlM1jiNUzUDB1ktBv6/lY+VgcoQ==", "integrity": "sha512-lAYeWvvhGYS6eQ5d0VEojw0juxGc3v4aAu8VLvMKWcZ1jXD13Bhc46u9Nvf4qAY6BAQsJDQcpEZLpzJu1bk1Qw==",
"requires": { "requires": {
"@sentry/types": "5.12.0", "@sentry/types": "5.26.0",
"@sentry/utils": "5.12.0", "@sentry/utils": "5.26.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "5.12.0", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.12.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.26.0.tgz",
"integrity": "sha512-fk73meyz4k4jCg9yzbma+WkggsfEIQWI2e2TWfYsRGcrV3RnlSrXyM4D91/A8Bjx10SNezHPUFHjasjlHXOkyA==", "integrity": "sha512-mdFo3FYaI1W3KEd8EHATYx8mDOZIxeoUhcBLlH7Iej6rKvdM7p8GoECrmHPU1l6sCCPtBuz66QT5YeXc7WILsA==",
"requires": { "requires": {
"@sentry/hub": "5.12.0", "@sentry/hub": "5.26.0",
"@sentry/types": "5.12.0", "@sentry/types": "5.26.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "5.12.0", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.12.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.26.0.tgz",
"integrity": "sha512-aZbBouBLrKB8wXlztriIagZNmsB+wegk1Jkl6eprqRW/w24Sl/47tiwH8c5S4jYTxdAiJk+SAR10AAuYmIN3zg==" "integrity": "sha512-ugpa1ePOhK55pjsyutAsa2tiJVQEyGYCaOXzaheg/3+EvhMdoW+owiZ8wupfvPhtZFIU3+FPOVz0d5k9K5d1rw=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "5.12.0", "version": "5.26.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.12.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.26.0.tgz",
"integrity": "sha512-fYUadGLbfTCbs4OG5hKCOtv2jrNE4/8LHNABy9DwNJ/t5DVtGqWAZBnxsC+FG6a3nVqCpxjFI9AHlYsJ2wsf7Q==", "integrity": "sha512-F2gnHIAWbjiowcAgxz3VpKxY/NQ39NTujEd/NPnRTWlRynLFg3bAV+UvZFXljhYJeN3b/zRlScNDcpCWTrtZGw==",
"requires": { "requires": {
"@sentry/types": "5.12.0", "@sentry/types": "5.26.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
@@ -2020,9 +2020,9 @@
} }
}, },
"@types/history": { "@types/history": {
"version": "4.7.6", "version": "4.7.8",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.6.tgz", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
"integrity": "sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w==", "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==",
"dev": true "dev": true
}, },
"@types/hoist-non-react-statics": { "@types/hoist-non-react-statics": {
@@ -2073,9 +2073,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "14.0.13", "version": "14.11.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz",
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==", "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==",
"dev": true "dev": true
}, },
"@types/parse-json": { "@types/parse-json": {
@@ -2097,13 +2097,13 @@
"dev": true "dev": true
}, },
"@types/react": { "@types/react": {
"version": "16.9.36", "version": "16.9.52",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.36.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.52.tgz",
"integrity": "sha512-mGgUb/Rk/vGx4NCvquRuSH0GHBQKb1OqpGS9cT9lFxlTLHZgkksgI60TuIxubmn7JuCb+sENHhQciqa0npm0AQ==", "integrity": "sha512-EHRjmnxiNivwhGdMh9sz1Yw9AUxTSZFxKqdBWAAzyZx3sufWwx6ogqHYh/WB1m/I4ZpjkoZLExF5QTy2ekVi/Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^2.2.0" "csstype": "^3.0.2"
} }
}, },
"@types/react-dom": { "@types/react-dom": {
@@ -2128,9 +2128,9 @@
} }
}, },
"@types/react-router": { "@types/react-router": {
"version": "5.1.7", "version": "5.1.8",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.7.tgz", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
"integrity": "sha512-2ouP76VQafKjtuc0ShpwUebhHwJo0G6rhahW9Pb8au3tQTjYXd2jta4wv6U2tGLR/I42yuG00+UXjNYY0dTzbg==", "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/history": "*", "@types/history": "*",
@@ -2138,9 +2138,9 @@
} }
}, },
"@types/react-router-dom": { "@types/react-router-dom": {
"version": "5.1.5", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.5.tgz", "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz",
"integrity": "sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==", "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/history": "*", "@types/history": "*",
@@ -4934,9 +4934,10 @@
} }
}, },
"csstype": { "csstype": {
"version": "2.6.10", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
"integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==",
"dev": true
}, },
"currently-unhandled": { "currently-unhandled": {
"version": "0.4.1", "version": "0.4.1",
@@ -5327,15 +5328,6 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"dom-helpers": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
"integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^2.6.7"
}
},
"dom-serializer": { "dom-serializer": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -9770,9 +9762,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.15", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true "dev": true
}, },
"lodash._reinterpolate": { "lodash._reinterpolate": {
@@ -12680,9 +12672,9 @@
} }
}, },
"react": { "react": {
"version": "16.13.1", "version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"requires": { "requires": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -12999,9 +12991,9 @@
} }
}, },
"react-dom": { "react-dom": {
"version": "16.13.1", "version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
"requires": { "requires": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -13030,9 +13022,9 @@
} }
}, },
"react-redux": { "react-redux": {
"version": "7.2.0", "version": "7.2.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz",
"integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==", "integrity": "sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==",
"requires": { "requires": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"hoist-non-react-statics": "^3.3.0", "hoist-non-react-statics": "^3.3.0",
@@ -13156,17 +13148,6 @@
} }
} }
}, },
"react-transition-group": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
"integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"read-pkg": { "read-pkg": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+11 -12
View File
@@ -3,26 +3,25 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.28", "@fortawesome/fontawesome-svg-core": "1.2.32",
"@fortawesome/free-brands-svg-icons": "5.13.0", "@fortawesome/free-brands-svg-icons": "5.15.1",
"@fortawesome/free-solid-svg-icons": "5.13.0", "@fortawesome/free-solid-svg-icons": "5.15.1",
"@fortawesome/react-fontawesome": "0.1.10", "@fortawesome/react-fontawesome": "0.1.11",
"@sentry/browser": "^5.7.1", "@sentry/browser": "5.26.0",
"bbcode-to-react": "0.2.9", "bbcode-to-react": "0.2.9",
"react": "16.14.0", "react": "16.14.0",
"react-dom": "16.14.0", "react-dom": "16.14.0",
"react-loader-spinner": "3.1.14", "react-loader-spinner": "3.1.14",
"react-redux": "7.2.0", "redux": "4.0.5",
"react-router-dom": "5.2.0", "react-redux": "7.2.1",
"react-transition-group": "4.4.1", "react-router-dom": "5.2.0"
"redux": "4.0.5"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "14.0.13", "@types/node": "14.11.8",
"@types/react": "16.9.36", "@types/react": "16.9.52",
"@types/react-dom": "16.9.8", "@types/react-dom": "16.9.8",
"@types/react-redux": "7.1.9", "@types/react-redux": "7.1.9",
"@types/react-router-dom": "5.1.5", "@types/react-router-dom": "5.1.6",
"@types/react-transition-group": "4.4.0", "@types/react-transition-group": "4.4.0",
"node-sass": "4.14.1", "node-sass": "4.14.1",
"redux-devtools-extension": "2.13.8", "redux-devtools-extension": "2.13.8",
+3
View File
@@ -29,12 +29,15 @@ async function request<T>(url: string, method: string, body?: string | FormData)
function get<T>(url: string, data?: object): Promise<T> { function get<T>(url: string, data?: object): Promise<T> {
return request<T>(url, 'GET', JSON.stringify(data)); return request<T>(url, 'GET', JSON.stringify(data));
} }
function post<T>(url: string, data?: object | FormData): Promise<T> { function post<T>(url: string, data?: object | FormData): Promise<T> {
return request(url, 'POST', data instanceof FormData ? data : JSON.stringify(data)); return request(url, 'POST', data instanceof FormData ? data : JSON.stringify(data));
} }
function put<T>(url: string, data?: object): Promise<T> { function put<T>(url: string, data?: object): Promise<T> {
return request<T>(url, 'PUT', JSON.stringify(data)); return request<T>(url, 'PUT', JSON.stringify(data));
} }
function del<T>(url: string, data?: object): Promise<T> { function del<T>(url: string, data?: object): Promise<T> {
return request<T>(url, 'DELETE', JSON.stringify(data)); return request<T>(url, 'DELETE', JSON.stringify(data));
} }
+11 -2
View File
@@ -1,6 +1,15 @@
import { post } from "./api"; import { post } from "./api";
export const restore = (token: string, new_password: string) => post<string>('auth/restore', { token, new_password }); export const restore = (token: string, new_password: string) => post<string>('auth/restore', { token, new_password });
export const registerUser = (login: string, password: string, email: string, recaptcha: string) => post<UserType>('auth/register', { login, password, email, recaptcha }); export const registerUser = (login: string, password: string, email: string, recaptcha: string) => post<UserType>('auth/register', {
export const login = (login: string, password: string, recaptcha: string) => post<UserType>('auth/login', { login, password, recaptcha }); login,
password,
email,
recaptcha
});
export const login = (login: string, password: string, recaptcha: string) => post<UserType>('auth/login', {
login,
password,
recaptcha
});
export const userLogout = () => post<string>('auth/logout'); export const userLogout = () => post<string>('auth/logout');
+5 -2
View File
@@ -1,4 +1,4 @@
import { get, post, put, del } from "./api"; import { del, get, post, put } from "./api";
export const fetchNovels = (orderBy?: string) => get<NovelType[]>(orderBy ? `novels?sort=${orderBy}` : 'novels'); export const fetchNovels = (orderBy?: string) => get<NovelType[]>(orderBy ? `novels?sort=${orderBy}` : 'novels');
@@ -15,6 +15,9 @@ export const fetchNovelComments = (id: number, offset=0, count=5) => get<Comment
export const postNovelComment = (id: number, text: string) => post<CommentType>(`novels/${id}/comments`, { text }); export const postNovelComment = (id: number, text: string) => post<CommentType>(`novels/${id}/comments`, { text });
export const fetchUserNovel = (userId: number, novelId: number) => get<UserNovelType>(`users/${userId}/novels/${novelId}`); export const fetchUserNovel = (userId: number, novelId: number) => get<UserNovelType>(`users/${userId}/novels/${novelId}`);
export const createUserNovel = (userId: number, novelId: number, status='planned') => post<UserNovelType>(`users/${userId}/novels`, { novel_id: Number(novelId), status }); export const createUserNovel = (userId: number, novelId: number, status = 'planned') => post<UserNovelType>(`users/${userId}/novels`, {
novel_id: Number(novelId),
status
});
export const updateUserNovel = (userId: number, novelId: number, data: { mark?: number; status?: string }) => put<UserNovelType>(`users/${userId}/novels/${novelId}`, data); export const updateUserNovel = (userId: number, novelId: number, data: { mark?: number; status?: string }) => put<UserNovelType>(`users/${userId}/novels/${novelId}`, data);
export const deleteUserNovel = (userId: number, novelId: number) => del(`users/${userId}/novels/${novelId}`); export const deleteUserNovel = (userId: number, novelId: number) => del(`users/${userId}/novels/${novelId}`);
+8 -2
View File
@@ -12,8 +12,14 @@ export const activateAccount = (token: string) => post<string>('auth/activate',
export const generateActivateLink = () => post<string>('auth/activateToken'); export const generateActivateLink = () => post<string>('auth/activateToken');
export const fetchUserSettings = () => get<UserSettingsType>('users/me/settings'); export const fetchUserSettings = () => get<UserSettingsType>('users/me/settings');
export const updateUserGeneralSettings = (use_gravatar: boolean, avatar: string) => post<UserType>('users/me/settings', { use_gravatar, avatar }); export const updateUserGeneralSettings = (use_gravatar: boolean, avatar: string) => post<UserType>('users/me/settings', {
use_gravatar,
avatar
});
export const updateUserAppearanceSettings = (theme: string) => post<UserType>('users/me/settings', { theme }); export const updateUserAppearanceSettings = (theme: string) => post<UserType>('users/me/settings', { theme });
export const uploadAvatar = (avatar: FormData) => post<string>('users/me/avatar', avatar); export const uploadAvatar = (avatar: FormData) => post<string>('users/me/avatar', avatar);
export const generateRestoreLink = (email: string, recaptcha: string) => post('auth/restoreToken', { email, recaptcha }); export const generateRestoreLink = (email: string, recaptcha: string) => post('auth/restoreToken', {
email,
recaptcha
});
+6 -2
View File
@@ -35,7 +35,10 @@ class ActivateAccount extends React.Component<Props, State> {
if(msg) { if(msg) {
this.setState({ msg, error: true }); this.setState({ msg, error: true });
} else { } else {
this.setState({ msg: 'Во время активации произошла ошибка! Сообщите об этом разработчику!', error: true }); this.setState({
msg: 'Во время активации произошла ошибка! Сообщите об этом разработчику!',
error: true
});
console.error(err.text); console.error(err.text);
} }
if(err.code === 5) { if(err.code === 5) {
@@ -55,7 +58,8 @@ class ActivateAccount extends React.Component<Props, State> {
const { msg, error } = this.state; const { msg, error } = this.state;
if(msg.length === 0) return <Loading/>; if(msg.length === 0) return <Loading/>;
return ( return (
<div className={'Error'} style={{ background: error ? 'var(--error-bg-color)' : 'var(--success-bg-color)' }}>{msg}</div> <div className={'Error'}
style={{ background: error ? 'var(--error-bg-color)' : 'var(--success-bg-color)' }}>{msg}</div>
); );
} }
} }
+11 -4
View File
@@ -26,9 +26,15 @@ class AdminPanel extends React.Component<Props, State> {
const subPath = pathname.slice(path.length + 1); const subPath = pathname.slice(path.length + 1);
let component; let component;
switch(subPath) { switch(subPath) {
case 'users': component = <AdminPanelUsers path={`${path}/${subPath}`}/>; break; case 'users':
case 'novels': component = <AdminPanelNovels/>; break; component = <AdminPanelUsers path={`${path}/${subPath}`}/>;
case 'news': component = <APNews path={`${path}/${subPath}`}/>; break; break;
case 'novels':
component = <AdminPanelNovels/>;
break;
case 'news':
component = <APNews path={`${path}/${subPath}`}/>;
break;
default: { default: {
const users = /users\/(\d+)/; const users = /users\/(\d+)/;
const news = /news\/(\d+)/; const news = /news\/(\d+)/;
@@ -43,7 +49,8 @@ class AdminPanel extends React.Component<Props, State> {
return ( return (
<div className={'AdminPanel'}> <div className={'AdminPanel'}>
<div className="AdminPanel__Title">Админ панель <span style={{ color: 'var(--error-bg-color)' }}>(в разработке)</span></div> <div className="AdminPanel__Title">Админ панель <span style={{ color: 'var(--error-bg-color)' }}>(в разработке)</span>
</div>
<div className="AdminPanel__Router"> <div className="AdminPanel__Router">
<Link to={path}>Главная</Link> <Link to={path}>Главная</Link>
<Link to={`${path}/users`}>Пользователи</Link> <Link to={`${path}/users`}>Пользователи</Link>
+4 -4
View File
@@ -31,13 +31,12 @@ type State = {
}; };
class APModifyPost extends React.Component<Props, State> { class APModifyPost extends React.Component<Props, State> {
private shortPost = React.createRef<HTMLTextAreaElement>();
private fullPost = React.createRef<HTMLTextAreaElement>();
state: State = { state: State = {
post: null, title: '', post: null, title: '',
previewShort: '', previewFull: '' previewShort: '', previewFull: ''
} }
private shortPost = React.createRef<HTMLTextAreaElement>();
private fullPost = React.createRef<HTMLTextAreaElement>();
componentDidMount() { componentDidMount() {
const { postId } = this.props; const { postId } = this.props;
@@ -135,7 +134,8 @@ class APModifyPost extends React.Component<Props, State> {
<div> <div>
<Button style={{ marginRight: '1rem' }}>Сохранить</Button> <Button style={{ marginRight: '1rem' }}>Сохранить</Button>
<Button type={"button"} style={{ color: 'var(--error-bg-color)'}} onClick={this.deletePost}>Удалить</Button> <Button type={"button"} style={{ color: 'var(--error-bg-color)' }}
onClick={this.deletePost}>Удалить</Button>
</div> </div>
</form> </form>
</Group> </Group>
@@ -1,13 +1,9 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
type Props = { type Props = {};
}; type State = {};
type State = {
};
class AdminPanelNovels extends React.Component<Props, State> { class AdminPanelNovels extends React.Component<Props, State> {
render() { render() {
+10 -5
View File
@@ -65,12 +65,17 @@ class ModifyUser extends React.Component<Props, State> {
<div className={'APUser__Modify'}> <div className={'APUser__Modify'}>
<div className={'APUser__Modify_Title'}>{username}</div> <div className={'APUser__Modify_Title'}>{username}</div>
<form className={'APUser__Modify_Form'} onSubmit={this.onSubmit}> <form className={'APUser__Modify_Form'} onSubmit={this.onSubmit}>
<label>Имя пользователя: <input type="text" onChange={this.onChange} name={'username'} value={user.username}/></label> <label>Имя пользователя: <input type="text" onChange={this.onChange} name={'username'}
<label>Email: <input type='email' onChange={this.onChange} name={'email'} value={user.email}/></label> value={user.username}/></label>
<label>Аватар: <input type='text' onChange={this.onChange} name={'avatar'} value={user.avatar}/></label> <label>Email: <input type='email' onChange={this.onChange} name={'email'}
value={user.email}/></label>
<label>Аватар: <input type='text' onChange={this.onChange} name={'avatar'}
value={user.avatar}/></label>
<label>Суперюзер: <input type='checkbox' onChange={this.onChange} name={'is_superuser'} checked={user.is_superuser}/></label> <label>Суперюзер: <input type='checkbox' onChange={this.onChange} name={'is_superuser'}
<label>Активирован: <input type='checkbox' onChange={this.onChange} name={'is_activated'} checked={user.is_activated}/></label> checked={user.is_superuser}/></label>
<label>Активирован: <input type='checkbox' onChange={this.onChange} name={'is_activated'}
checked={user.is_activated}/></label>
<div style={{ textAlign: "right" }}> <div style={{ textAlign: "right" }}>
<Button>Сохранить!</Button> <Button>Сохранить!</Button>
+1 -1
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import {Switch, Route} from "react-router-dom"; import { Route, Switch } from "react-router-dom";
import './App.sass'; import './App.sass';
import Navbar from "../Navbar/Navbar"; import Navbar from "../Navbar/Navbar";
import Novel from "../Novel/Novel"; import Novel from "../Novel/Novel";
+14 -4
View File
@@ -1,6 +1,13 @@
import React from 'react'; import React from 'react';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBold, faExclamationTriangle, faItalic, faStrikethrough, faUnderline, faTint } from "@fortawesome/free-solid-svg-icons"; import {
faBold,
faExclamationTriangle,
faItalic,
faStrikethrough,
faTint,
faUnderline
} from "@fortawesome/free-solid-svg-icons";
import Comment from '../../ui/Comment/Comment'; import Comment from '../../ui/Comment/Comment';
import './Comments.sass'; import './Comments.sass';
import Loading from "../../ui/Loading"; import Loading from "../../ui/Loading";
@@ -93,8 +100,10 @@ class Comments extends React.Component<Props, State> {
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 'i')} icon={faItalic}/> <FontAwesomeIcon onClick={event => this.addTagToComment(event, 'i')} icon={faItalic}/>
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 'u')} icon={faUnderline}/> <FontAwesomeIcon onClick={event => this.addTagToComment(event, 'u')} icon={faUnderline}/>
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 's')} icon={faStrikethrough}/> <FontAwesomeIcon onClick={event => this.addTagToComment(event, 's')} icon={faStrikethrough}/>
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 'spoiler')} icon={faExclamationTriangle}/> <FontAwesomeIcon onClick={event => this.addTagToComment(event, 'spoiler')}
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 'color', 'white')} icon={faTint}/> icon={faExclamationTriangle}/>
<FontAwesomeIcon onClick={event => this.addTagToComment(event, 'color', 'white')}
icon={faTint}/>
</div> </div>
<textarea <textarea
className={'Comments_TextArea'} className={'Comments_TextArea'}
@@ -115,7 +124,8 @@ class Comments extends React.Component<Props, State> {
<Loading/> <Loading/>
)} )}
{comments && comments.length > 0 ? ( {comments && comments.length > 0 ? (
comments.map(comment => <Comment text={comment.text} key={comment.id} user={users.get(comment.user_id)} />) comments.map(comment => <Comment text={comment.text} key={comment.id}
user={users.get(comment.user_id)}/>)
) : ( ) : (
'Никто не оставил комментарий к этой новелле... :c' 'Никто не оставил комментарий к этой новелле... :c'
)} )}
+1 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
import './Footer.sass'; import './Footer.sass';
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faDiscord, faVk, faTelegramPlane } from '@fortawesome/free-brands-svg-icons'; import { faDiscord, faTelegramPlane, faVk } from '@fortawesome/free-brands-svg-icons';
const Footer: React.FC = () => { const Footer: React.FC = () => {
return ( return (
+1
View File
@@ -42,5 +42,6 @@
p p
margin: 0 .2rem margin: 0 .2rem
* *
margin: 0 margin: 0
+1 -1
View File
@@ -1,4 +1,4 @@
import React, {FormEvent, ChangeEvent} from "react"; import React, { ChangeEvent, FormEvent } from "react";
import Modal from "../../ui/Modal/Modal"; import Modal from "../../ui/Modal/Modal";
import { login } from "../../api/auth"; import { login } from "../../api/auth";
import { connect } from "react-redux"; import { connect } from "react-redux";
+1
View File
@@ -45,5 +45,6 @@
p p
margin: 0 .2rem margin: 0 .2rem
* *
margin: 0 margin: 0
+3 -2
View File
@@ -25,10 +25,10 @@ type State = {
} }
class RegisterModal extends React.Component<Props, State> { class RegisterModal extends React.Component<Props, State> {
private recaptcha: any;
state = { state = {
error: '', login: '', password1: '', password2: '', email: '', recaptcha: '' error: '', login: '', password1: '', password2: '', email: '', recaptcha: ''
}; };
private recaptcha: any;
handlePassword1Change = (event: any) => this.setState({ password1: event.target.value }); handlePassword1Change = (event: any) => this.setState({ password1: event.target.value });
handlePassword2Change = (event: any) => this.setState({ password2: event.target.value }); handlePassword2Change = (event: any) => this.setState({ password2: event.target.value });
@@ -55,7 +55,8 @@ class RegisterModal extends React.Component<Props, State> {
const notification = ( const notification = (
<NotificationMessage level={"success"}> <NotificationMessage level={"success"}>
Аккаунт успешно создан! Аккаунт успешно создан!
Проверьте почту <strong>{email}</strong>. На неё была отправлена ссылка для подтверждения аккаунта. Проверьте почту <strong>{email}</strong>. На неё была отправлена ссылка для подтверждения
аккаунта.
</NotificationMessage> </NotificationMessage>
); );
const { addNotification, setModal } = this.props; const { addNotification, setModal } = this.props;
+8 -4
View File
@@ -32,7 +32,8 @@ class Navbar extends React.Component<Props, State> {
localStorage.removeItem('user'); localStorage.removeItem('user');
localStorage.removeItem('theme'); localStorage.removeItem('theme');
document.querySelector('body')!!.removeAttribute('theme'); document.querySelector('body')!!.removeAttribute('theme');
userLogout().catch(() => {}); userLogout().catch(() => {
});
}; };
changeSize = () => { changeSize = () => {
@@ -61,7 +62,8 @@ class Navbar extends React.Component<Props, State> {
Новеллы Новеллы
</Link> </Link>
{expand && isMinimized && ( {expand && isMinimized && (
<div onClick={this.changeSize} className={'Navbar_Button'} style={{ position: "absolute", right: 0, margin: '.5rem' }}>X</div> <div onClick={this.changeSize} className={'Navbar_Button'}
style={{ position: "absolute", right: 0, margin: '.5rem' }}>X</div>
)} )}
</div> </div>
); );
@@ -69,7 +71,8 @@ class Navbar extends React.Component<Props, State> {
const user = isMinimized ? ( const user = isMinimized ? (
<div className={'Navbar__Dropdown'}> <div className={'Navbar__Dropdown'}>
<div className="Navbar__Dropdown_Button Navbar__User_Profile"> <div className="Navbar__Dropdown_Button Navbar__User_Profile">
<div className={'Navbar__User_Avatar'} style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/> <div className={'Navbar__User_Avatar'}
style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/>
<div>{this.props.currentUser.username}</div> <div>{this.props.currentUser.username}</div>
</div> </div>
<div className="Navbar__Dropdown_Menu"> <div className="Navbar__Dropdown_Menu">
@@ -85,7 +88,8 @@ class Navbar extends React.Component<Props, State> {
</div> </div>
) : (<> ) : (<>
<Link to={`/user/${currentUser.id}`} className="Navbar__User_Profile"> <Link to={`/user/${currentUser.id}`} className="Navbar__User_Profile">
<div className={'Navbar__User_Avatar'} style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/> <div className={'Navbar__User_Avatar'}
style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/>
<div>{this.props.currentUser.username}</div> <div>{this.props.currentUser.username}</div>
</Link> </Link>
{currentUser.group && hasAccessToAdminPanel(currentUser) && ( {currentUser.group && hasAccessToAdminPanel(currentUser) && (
+21 -10
View File
@@ -3,13 +3,15 @@ import './Novel.sass'
import NotFoundError from "../NotFoundError"; import NotFoundError from "../NotFoundError";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { import {
createUserNovel,
deleteUserNovel,
fetchNovel, fetchNovel,
fetchNovelCharacters, fetchNovelCharacters,
fetchNovelComments, fetchNovelComments,
fetchNovelGenres,
fetchUserNovel, fetchUserNovel,
updateUserNovel, postNovelComment,
deleteUserNovel, updateUserNovel
fetchNovelGenres, postNovelComment, createUserNovel
} from "../../api/novels"; } from "../../api/novels";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Loading from "../../ui/Loading"; import Loading from "../../ui/Loading";
@@ -169,8 +171,10 @@ class Novel extends React.Component<Props, State> {
<div className={'Novel__Main_Status'}> <div className={'Novel__Main_Status'}>
<div className="Novel__Main_Status_Primary"> <div className="Novel__Main_Status_Primary">
{!userData {!userData
? <div className={'Novel__Main_Status_Element'} onClick={() => this.updateNovelStatus('planned')}>Добавить в список</div> ? <div className={'Novel__Main_Status_Element'}
: <div className={"Novel__Main_Status_Element"}>{TranslateStatus[userData.status]}</div>} onClick={() => this.updateNovelStatus('planned')}>Добавить в список</div>
: <div
className={"Novel__Main_Status_Element"}>{TranslateStatus[userData.status]}</div>}
{userData && ( {userData && (
<div <div
className={'Novel__Main_Status_Element'} className={'Novel__Main_Status_Element'}
@@ -184,9 +188,12 @@ class Novel extends React.Component<Props, State> {
{statusExpanded && {statusExpanded &&
<div className="Novel__Main_Status_Extended"> <div className="Novel__Main_Status_Extended">
{userData && Object.entries(TranslateStatus).filter(([eng]) => eng !== userData.status).map(([eng, rus]) => ( {userData && Object.entries(TranslateStatus).filter(([eng]) => eng !== userData.status).map(([eng, rus]) => (
<div key={eng} className="Novel__Main_Status_Element" onClick={() => this.updateNovelStatus(eng)}>{rus}</div> <div key={eng} className="Novel__Main_Status_Element"
onClick={() => this.updateNovelStatus(eng)}>{rus}</div>
))} ))}
<div className={"Novel__Main_Status_Element"} style={{ color: 'red' }} onClick={this.deleteNovelStatus}>Удалить из списка</div> <div className={"Novel__Main_Status_Element"} style={{ color: 'red' }}
onClick={this.deleteNovelStatus}>Удалить из списка
</div>
</div>} </div>}
<div className={'Novel__Main_Status_Mark'}> <div className={'Novel__Main_Status_Mark'}>
{userData && [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(mark => ( {userData && [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(mark => (
@@ -208,7 +215,8 @@ class Novel extends React.Component<Props, State> {
</div>} </div>}
{genres && genres.length > 0 && {genres && genres.length > 0 &&
<div className="Novel__Info_Genres"> <div className="Novel__Info_Genres">
<strong>Жанры</strong>: {genres.map(genre => <div className={'Genre'} key={genre.id}>{genre.localized_name}</div>)} <strong>Жанры</strong>: {genres.map(genre => <div className={'Genre'}
key={genre.id}>{genre.localized_name}</div>)}
</div>} </div>}
<div> <div>
<strong>Год выхода</strong>: {novel.release_date.toLocaleDateString()} <strong>Год выхода</strong>: {novel.release_date.toLocaleDateString()}
@@ -230,11 +238,14 @@ class Novel extends React.Component<Props, State> {
{this.state.characters.filter(char => char.main).map(char => {this.state.characters.filter(char => char.main).map(char =>
<div className={'Novel__Character'} key={char.id}> <div className={'Novel__Character'} key={char.id}>
<div className='Character_Image' style={{ backgroundImage: `url(${char.image})` }}/> <div className='Character_Image' style={{ backgroundImage: `url(${char.image})` }}/>
<Link to={`/character/${char.id}`}><div className={'Novel__Character_Name'}>{char.localized_name}</div></Link> <Link to={`/character/${char.id}`}>
<div className={'Novel__Character_Name'}>{char.localized_name}</div>
</Link>
</div>)} </div>)}
</div> </div>
</div>} </div>}
<Comments user={currentUser} comments={comments} sendComment={this.sendComment} loadMore={this.loadMoreComments} hasMore={hasMoreComments} /> <Comments user={currentUser} comments={comments} sendComment={this.sendComment}
loadMore={this.loadMoreComments} hasMore={hasMoreComments}/>
</div> </div>
); );
} }
@@ -12,11 +12,10 @@ type State = {
} }
class PasswordRestoreGenerate extends React.Component<{}, State> { class PasswordRestoreGenerate extends React.Component<{}, State> {
private recaptcha: any;
state: State = { state: State = {
email: '', recaptcha: '' email: '', recaptcha: ''
} }
private recaptcha: any;
onSubmit = (event: React.FormEvent) => { onSubmit = (event: React.FormEvent) => {
event.preventDefault(); event.preventDefault();
+5 -3
View File
@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import './UserNovels.sass'; import './UserNovels.sass';
import { fetchUserNovels, fetchUser } from '../../../api/users'; import { fetchUser, fetchUserNovels } from '../../../api/users';
import Loading from '../../../ui/Loading'; import Loading from '../../../ui/Loading';
import NovelItem from '../../../ui/NovelItem/NovelItem'; import NovelItem from '../../../ui/NovelItem/NovelItem';
import { capitalize, generateClassName, getRandomInt } from "../../../utils"; import { capitalize, generateClassName, getRandomInt } from "../../../utils";
@@ -77,7 +77,8 @@ class UserNovels extends React.Component<Props, State> {
<Button onClick={this.getRandomNovel}>Рандомная новелла</Button> <Button onClick={this.getRandomNovel}>Рандомная новелла</Button>
</div> </div>
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}> <div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
{planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id+1} viewType={viewType}/>)} {planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1}
viewType={viewType}/>)}
</div> </div>
</div>} </div>}
@@ -86,7 +87,8 @@ class UserNovels extends React.Component<Props, State> {
Пройденные Пройденные
</div> </div>
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}> <div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
{completed.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id+1} viewType={viewType}/>)} {completed.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1}
viewType={viewType}/>)}
</div> </div>
</div>} </div>}
</div> </div>
+5 -2
View File
@@ -49,8 +49,11 @@ class General extends React.Component<Props, State> {
return ( return (
<Group title={'Аватар'} className={'Settings_Group'}> <Group title={'Аватар'} className={'Settings_Group'}>
<form onSubmit={this.saveAvatar}> <form onSubmit={this.saveAvatar}>
<label>Использовать Gravatar <input type="checkbox" onChange={event => this.setState({ useGravatar: event.target.checked })} checked={useGravatar}/></label> <label>Использовать Gravatar <input type="checkbox"
<label>Загрузить аватар <input type="file" onChange={this.onChangeAvatar} disabled={useGravatar}/></label> onChange={event => this.setState({ useGravatar: event.target.checked })}
checked={useGravatar}/></label>
<label>Загрузить аватар <input type="file" onChange={this.onChangeAvatar}
disabled={useGravatar}/></label>
<Button>Сохранить</Button> <Button>Сохранить</Button>
</form> </form>
</Group> </Group>
+35 -13
View File
@@ -85,27 +85,38 @@ class User extends React.Component<Props, State> {
{!user.is_activated && user.id === currentUser.id && ( {!user.is_activated && user.id === currentUser.id && (
<div className={'User__Activate'}> <div className={'User__Activate'}>
<h2>Ваш аккаунт не активирован!</h2> <h2>Ваш аккаунт не активирован!</h2>
<p>Активируйте аккаунт, для доступа ко многим функциям. Так же актвация аккаунта позволит восстановить пароль.</p> <p>Активируйте аккаунт, для доступа ко многим функциям. Так же актвация аккаунта позволит
восстановить пароль.</p>
<div style={{ display: "flex", justifyContent: "flex-end" }}> <div style={{ display: "flex", justifyContent: "flex-end" }}>
<Button onClick={this.sendActivateLink} style={{ fontSize: '1.2rem' }}>Активировать!</Button> <Button onClick={this.sendActivateLink}
style={{ fontSize: '1.2rem' }}>Активировать!</Button>
</div> </div>
</div> </div>
)} )}
<div className="User__Info" style={style}> <div className="User__Info" style={style}>
{user.cover ? ( {user.cover ? (
<div className="User__Info_Cover User__Info_Main" style={{ backgroundImage: `url(${user.cover})` }}> <div className="User__Info_Cover User__Info_Main"
<div className="User__Info_Avatar" style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }} /> style={{ backgroundImage: `url(${user.cover})` }}>
<div className="User__Info_Avatar"
style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }}/>
<div> <div>
<div className='User__Info_Nick'>{user.username}</div> <div className='User__Info_Nick'>{user.username}</div>
<div className='User__Info_Group' style={{ borderColor: user.group.color, color: user.group.color }}>{user.group.name}</div> <div className='User__Info_Group' style={{
borderColor: user.group.color,
color: user.group.color
}}>{user.group.name}</div>
</div> </div>
</div>) : ( </div>) : (
<div className={'User__Info_Main'}> <div className={'User__Info_Main'}>
<div className="User__Info_Avatar" style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }} /> <div className="User__Info_Avatar"
style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }}/>
<div> <div>
<div className='User__Info_Nick'>{user.username}</div> <div className='User__Info_Nick'>{user.username}</div>
<div className='User__Info_Group' style={{ borderColor: user.group.color, color: user.group.color }}>{user.group.name}</div> <div className='User__Info_Group' style={{
borderColor: user.group.color,
color: user.group.color
}}>{user.group.name}</div>
</div> </div>
</div> </div>
)} )}
@@ -115,11 +126,21 @@ class User extends React.Component<Props, State> {
<div className="User__Statistic_Item"> <div className="User__Statistic_Item">
<div className="User__Statistic_Title">Новеллы</div> <div className="User__Statistic_Title">Новеллы</div>
<div className="User__Statistic_Blocks"> <div className="User__Statistic_Blocks">
<div className="User__Statistic_Block Planned" data-count={novels.filter(n => n.status === 'planned').length}>Запланированно</div> <div className="User__Statistic_Block Planned"
<div className="User__Statistic_Block Completed" data-count={novels.filter(n => n.status === 'completed').length}>Пройдено</div> data-count={novels.filter(n => n.status === 'planned').length}>Запланированно
<div className="User__Statistic_Block InProgress" data-count={novels.filter(n => n.status === 'in_progress').length}>Прохожу</div> </div>
<div className="User__Statistic_Block Deferred" data-count={novels.filter(n => n.status === 'deferred').length}>Отложено</div> <div className="User__Statistic_Block Completed"
<div className="User__Statistic_Block Dropped" data-count={novels.filter(n => n.status === 'dropped').length}>Брошено</div> data-count={novels.filter(n => n.status === 'completed').length}>Пройдено
</div>
<div className="User__Statistic_Block InProgress"
data-count={novels.filter(n => n.status === 'in_progress').length}>Прохожу
</div>
<div className="User__Statistic_Block Deferred"
data-count={novels.filter(n => n.status === 'deferred').length}>Отложено
</div>
<div className="User__Statistic_Block Dropped"
data-count={novels.filter(n => n.status === 'dropped').length}>Брошено
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -131,7 +152,8 @@ class User extends React.Component<Props, State> {
</div> </div>
<div className="User__List_Items"> <div className="User__List_Items">
{novels.length > 0 ? {novels.length > 0 ?
novels.slice(0, 4).map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id+1} viewType={'grid'} /> novels.slice(0, 4).map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1}
viewType={'grid'}/>
) : ( ) : (
<div>У этого пользователя нет новелл!</div> <div>У этого пользователя нет новелл!</div>
)} )}
+3 -4
View File
@@ -4,9 +4,7 @@ import Loading from "../../ui/Loading";
import './Users.sass'; import './Users.sass';
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
type Props = { type Props = {}
}
type State = { type State = {
users: UserType[] users: UserType[]
@@ -29,7 +27,8 @@ class Users extends React.Component<Props, State> {
<div className={'Users'}> <div className={'Users'}>
{users.map(user => ( {users.map(user => (
<Link className={'Users__User'} key={user.id} to={`user/${user.id}`}> <Link className={'Users__User'} key={user.id} to={`user/${user.id}`}>
<div className="Users__User_Avatar" style={{ backgroundImage: `url(${user.avatar}?s=96?t=${new Date().getTime()})` }}/> <div className="Users__User_Avatar"
style={{ backgroundImage: `url(${user.avatar}?s=96?t=${new Date().getTime()})` }}/>
{user.username} {user.username}
</Link> </Link>
))} ))}
+2 -1
View File
@@ -18,7 +18,8 @@ const reducer = (state=initialState, action: Action) => {
case HIDE_MODAL: { case HIDE_MODAL: {
return null; return null;
} }
default: return state; default:
return state;
} }
} }
+2 -1
View File
@@ -16,7 +16,8 @@ const reducer = (state = initialState, action: Action) => {
return [...state.slice(1), action.notification]; return [...state.slice(1), action.notification];
return [...state, action.notification]; return [...state, action.notification];
} }
default: return state; default:
return state;
} }
} }
+2 -1
View File
@@ -18,7 +18,8 @@ type Props = {
const NovelItem = ({ id, original_name, localized_name, image, status, mark, viewType, novelId }: Props) => ( const NovelItem = ({ id, original_name, localized_name, image, status, mark, viewType, novelId }: Props) => (
<Link to={`/novels/${id}`} className={generateClassName("NovelItem", capitalize(viewType) || 'Grid')}> <Link to={`/novels/${id}`} className={generateClassName("NovelItem", capitalize(viewType) || 'Grid')}>
{viewType === 'grid' && (<> {viewType === 'grid' && (<>
<div className='NovelItem__Image' style={{ backgroundImage: `url(${image || '/static/img/no-image.png'})` }}/> <div className='NovelItem__Image'
style={{ backgroundImage: `url(${image || '/static/img/no-image.png'})` }}/>
<div className="NovelItem__Title"> <div className="NovelItem__Title">
{localized_name || original_name} {localized_name || original_name}
</div> </div>
+2
View File
@@ -113,7 +113,9 @@ $label-font-size-redo: $default-size * 4
&.progress-65 &.progress-65
@include draw-progress--solid(65, #e67e22, $bg-color) @include draw-progress--solid(65, #e67e22, $bg-color)
&.progress-25 &.progress-25
@include draw-progress--solid(25, #9b59b6, $bg-color) @include draw-progress--solid(25, #9b59b6, $bg-color)
&.progress-88 &.progress-88
@include draw-progress--solid(88, #3498db, $bg-color) @include draw-progress--solid(88, #3498db, $bg-color)
+2 -1
View File
@@ -9,7 +9,8 @@ const ProgressBar = ({ progress, color, alternative }: { progress: number, color
<div className={alternative ? 'pie-wrapper progress style-2' : "pie-wrapper progress-75"}> <div className={alternative ? 'pie-wrapper progress style-2' : "pie-wrapper progress-75"}>
<span className="label">{progress}<span className="percent">%</span></span> <span className="label">{progress}<span className="percent">%</span></span>
<div className="pie" style={progress <= 50 ? {} : { clip: 'rect(auto, auto, auto, auto)' }}> <div className="pie" style={progress <= 50 ? {} : { clip: 'rect(auto, auto, auto, auto)' }}>
<div className="left-side half-circle" style={{...borderColor, transform: `rotate(${progress*3.6}deg)`}}/> <div className="left-side half-circle"
style={{ ...borderColor, transform: `rotate(${progress * 3.6}deg)` }}/>
<div className="right-side half-circle" style={{ ...borderColor, ...rightSide }}/> <div className="right-side half-circle" style={{ ...borderColor, ...rightSide }}/>
</div> </div>
{alternative && <div className="shadow"/>} {alternative && <div className="shadow"/>}
+1 -1
View File
@@ -13,10 +13,10 @@ type State = {
} }
class Recaptcha extends React.Component<Props, State> { class Recaptcha extends React.Component<Props, State> {
private readonly interval: NodeJS.Timeout;
state: State = { state: State = {
isReady: false isReady: false
} }
private readonly interval: NodeJS.Timeout;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
+1 -1
View File
@@ -1,4 +1,4 @@
import React, {TextareaHTMLAttributes} from "react"; import React from "react";
import classNames from "../../classNames"; import classNames from "../../classNames";
import './TextField.sass'; import './TextField.sass';