From 38bb2af31dae060cd9502c9081b9d0a25467fd7f Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Sat, 19 Apr 2025 22:43:41 +0300 Subject: [PATCH 01/10] =?UTF-8?q?lecture-5=20=D1=80=D0=B5=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=94=D0=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-examples/comments.http | 2 +- api-examples/comments.json | 1814 ++++++++++++++++++++++++ src/app/article/index.js | 10 + src/components/comment/index.js | 55 + src/components/comment/style.css | 35 + src/components/comments-card/index.js | 77 + src/components/comments-card/style.css | 19 + src/store-redux/comments/actions.js | 23 + src/store-redux/comments/reducer.js | 26 + src/store-redux/exports.js | 1 + 10 files changed, 2061 insertions(+), 1 deletion(-) create mode 100644 api-examples/comments.json create mode 100644 src/components/comment/index.js create mode 100644 src/components/comment/style.css create mode 100644 src/components/comments-card/index.js create mode 100644 src/components/comments-card/style.css create mode 100644 src/store-redux/comments/actions.js create mode 100644 src/store-redux/comments/reducer.js diff --git a/api-examples/comments.http b/api-examples/comments.http index fd797fb83..a33b4c349 100644 --- a/api-examples/comments.http +++ b/api-examples/comments.http @@ -1,6 +1,6 @@ # Комментрии к товару -GET http://query.rest/api/v1/comments?fields=items(_id,text,dateCreate,author(profile(name)),parent(_id,_type),isDeleted),count&limit=*&search[parent]=670260bb7dd498df5525e5ed +GET http://query.rest/api/v1/comments?fields=items(_id,text,dateCreate,author(profile(name)),parent(_id,_type),isDeleted),count&limit=*&search[parent]=67fb67e98702ec3fc67fef7c # Остальные методы в http://query.rest/api/v1/docs/ (http://localhost:8010/api/v1/docs/) diff --git a/api-examples/comments.json b/api-examples/comments.json new file mode 100644 index 000000000..b28e747d0 --- /dev/null +++ b/api-examples/comments.json @@ -0,0 +1,1814 @@ +{ + "items": [ + { + "_id": "67fee024de177582e52335bb", + "text": "Первый коммент!", + "dateCreate": "2025-04-15T22:39:32.013Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67fee064de177582e52335c2", + "text": "Второй коммент!", + "dateCreate": "2025-04-15T22:40:36.407Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67fee07ede177582e52335c5", + "text": "Ответ на первый коммент!", + "dateCreate": "2025-04-15T22:41:02.889Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "67ff6be2de177582e52338d7", + "text": "d", + "dateCreate": "2025-04-16T08:35:46.413Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee07ede177582e52335c5", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "67ff953dde177582e5233a57", + "text": "", + "dateCreate": "2025-04-16T11:32:13.679Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": true + }, + { + "_id": "67ff9540de177582e5233a59", + "text": "jhjh\n", + "dateCreate": "2025-04-16T11:32:16.470Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ff95d2de177582e5233a6e", + "text": "hgthh\n", + "dateCreate": "2025-04-16T11:34:42.411Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ff95dede177582e5233a71", + "text": "jhjhj", + "dateCreate": "2025-04-16T11:34:54.911Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ff9644de177582e5233a90", + "text": "jhjj", + "dateCreate": "2025-04-16T11:36:36.379Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ffb9c8de177582e5233db3", + "text": "Hi\n", + "dateCreate": "2025-04-16T14:08:08.895Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ff953dde177582e5233a57", + "_type": "comment", + "isDeleted": true + }, + "isDeleted": false + }, + { + "_id": "67ffcbaa6bc0478e51e8e835", + "text": "GhbdTn", + "dateCreate": "2025-04-16T15:24:26.723Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ffcbb06bc0478e51e8e837", + "text": "ggggg", + "dateCreate": "2025-04-16T15:24:32.481Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ffcbc26bc0478e51e8e84a", + "text": "HELLO!", + "dateCreate": "2025-04-16T15:24:50.775Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ffd0ba6bc0478e51e8e890", + "text": "Ответ на это сообщение", + "dateCreate": "2025-04-16T15:46:02.067Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ffcbc26bc0478e51e8e84a", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "67ffd1206bc0478e51e8e89c", + "text": "11", + "dateCreate": "2025-04-16T15:47:44.523Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "67ffd3056bc0478e51e8e8b8", + "text": "Хай хай", + "dateCreate": "2025-04-16T15:55:49.436Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ffd1206bc0478e51e8e89c", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680000966bc0478e51e8ec2a", + "text": "fffff\n", + "dateCreate": "2025-04-16T19:10:14.940Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680006476bc0478e51e8ecf7", + "text": "ffff\n", + "dateCreate": "2025-04-16T19:34:31.501Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800064d6bc0478e51e8ecfa", + "text": "uuuu\n", + "dateCreate": "2025-04-16T19:34:37.987Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680006656bc0478e51e8ecfe", + "text": "f word\n\n", + "dateCreate": "2025-04-16T19:35:01.945Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680006b16bc0478e51e8ed04", + "text": "derf\n", + "dateCreate": "2025-04-16T19:36:17.830Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680006bc6bc0478e51e8ed06", + "text": "dfd", + "dateCreate": "2025-04-16T19:36:28.007Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680006476bc0478e51e8ecf7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680008376bc0478e51e8ed19", + "text": "Hm\n\n\n\n", + "dateCreate": "2025-04-16T19:42:47.583Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ffb9c8de177582e5233db3", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680008bc6bc0478e51e8ed26", + "text": "hjj", + "dateCreate": "2025-04-16T19:45:00.600Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ff6be2de177582e52338d7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68008dc06bc0478e51e8f313", + "text": "dasdad", + "dateCreate": "2025-04-17T05:12:32.799Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680094796bc0478e51e8f376", + "text": "a", + "dateCreate": "2025-04-17T05:41:13.930Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680094916bc0478e51e8f37a", + "text": "lol", + "dateCreate": "2025-04-17T05:41:37.310Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800aa406bc0478e51e8f588", + "text": "Z", + "dateCreate": "2025-04-17T07:14:08.879Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800aa4f6bc0478e51e8f58b", + "text": "z", + "dateCreate": "2025-04-17T07:14:23.510Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680008bc6bc0478e51e8ed26", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800ab3a6bc0478e51e8f5c3", + "text": "вфывфв", + "dateCreate": "2025-04-17T07:18:18.514Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800ac416bc0478e51e8f5ec", + "text": "d", + "dateCreate": "2025-04-17T07:22:41.172Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800ab3a6bc0478e51e8f5c3", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800ad286bc0478e51e8f5fe", + "text": "ф", + "dateCreate": "2025-04-17T07:26:32.697Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800ac416bc0478e51e8f5ec", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800adb46bc0478e51e8f611", + "text": "ф", + "dateCreate": "2025-04-17T07:28:52.775Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800064d6bc0478e51e8ecfa", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800adba6bc0478e51e8f616", + "text": "ф", + "dateCreate": "2025-04-17T07:28:58.717Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680006b16bc0478e51e8ed04", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800c3de6bc0478e51e8f8ab", + "text": "111", + "dateCreate": "2025-04-17T09:03:26.774Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800aa4f6bc0478e51e8f58b", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800e4fe6bc0478e51e8fe19", + "text": "123", + "dateCreate": "2025-04-17T11:24:46.962Z", + "author": { + "profile": { + "name": "User №10" + }, + "_id": "67fb67dc8702ec3fc67fe388" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800ecc26bc0478e51e90002", + "text": "gfdgdgdfg", + "dateCreate": "2025-04-17T11:57:54.218Z", + "author": { + "profile": { + "name": "User №10" + }, + "_id": "67fb67dc8702ec3fc67fe388" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800edc96bc0478e51e9006f", + "text": "абракадабра", + "dateCreate": "2025-04-17T12:02:17.174Z", + "author": { + "profile": { + "name": "User №8" + }, + "_id": "67fb67dc8702ec3fc67fe386" + }, + "parent": { + "_id": "67ffd0ba6bc0478e51e8e890", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f2326bc0478e51e9014f", + "text": "wwww52", + "dateCreate": "2025-04-17T12:21:06.025Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f3176bc0478e51e9019f", + "text": "wwwwwasdasd", + "dateCreate": "2025-04-17T12:24:55.856Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f3286bc0478e51e901ab", + "text": "asfasfasf", + "dateCreate": "2025-04-17T12:25:12.125Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800c3de6bc0478e51e8f8ab", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f3686bc0478e51e901bb", + "text": "wwww", + "dateCreate": "2025-04-17T12:26:16.039Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800f3286bc0478e51e901ab", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f36f6bc0478e51e901be", + "text": "dasdasd", + "dateCreate": "2025-04-17T12:26:23.350Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f3736bc0478e51e901c1", + "text": "sssssss", + "dateCreate": "2025-04-17T12:26:27.764Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f3816bc0478e51e901c4", + "text": "asdas", + "dateCreate": "2025-04-17T12:26:41.344Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f3886bc0478e51e901c7", + "text": "Ответ\n", + "dateCreate": "2025-04-17T12:26:48.517Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f7396bc0478e51e90306", + "text": "fdfd", + "dateCreate": "2025-04-17T12:42:33.210Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800f3686bc0478e51e901bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f74a6bc0478e51e90312", + "text": "dsfsdf", + "dateCreate": "2025-04-17T12:42:50.855Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800f3686bc0478e51e901bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f7556bc0478e51e90314", + "text": "sdsd", + "dateCreate": "2025-04-17T12:43:01.782Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f7e66bc0478e51e9032f", + "text": "ыыыы", + "dateCreate": "2025-04-17T12:45:26.858Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f7ef6bc0478e51e90337", + "text": "111", + "dateCreate": "2025-04-17T12:45:35.431Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800f7e66bc0478e51e9032f", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f7f26bc0478e51e90339", + "text": "2222", + "dateCreate": "2025-04-17T12:45:38.334Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6800f7e66bc0478e51e9032f", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6800f89a6bc0478e51e90388", + "text": "dsfd", + "dateCreate": "2025-04-17T12:48:26.777Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800f9966bc0478e51e903f0", + "text": "sadas", + "dateCreate": "2025-04-17T12:52:38.802Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6800ff066bc0478e51e90506", + "text": "dsfsdf", + "dateCreate": "2025-04-17T13:15:50.705Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680101bc6bc0478e51e905b7", + "text": "asfasf", + "dateCreate": "2025-04-17T13:27:24.846Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ff6be2de177582e52338d7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68010c406bc0478e51e907e0", + "text": "ввыыыыыыыыыыыыыыы", + "dateCreate": "2025-04-17T14:12:16.924Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68010d476bc0478e51e90815", + "text": "123", + "dateCreate": "2025-04-17T14:16:39.329Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68010d496bc0478e51e90817", + "text": "dasd", + "dateCreate": "2025-04-17T14:16:41.915Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "68010d476bc0478e51e90815", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68010d4c6bc0478e51e90819", + "text": "asfas", + "dateCreate": "2025-04-17T14:16:44.220Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "68010d496bc0478e51e90817", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68010d686bc0478e51e90832", + "text": "sadasd", + "dateCreate": "2025-04-17T14:17:12.892Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680112c36bc0478e51e90a4b", + "text": "fafdasf", + "dateCreate": "2025-04-17T14:40:03.513Z", + "author": { + "profile": { + "name": "User №3" + }, + "_id": "67fb67dc8702ec3fc67fe381" + }, + "parent": { + "_id": "6800f74a6bc0478e51e90312", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680112ff6bc0478e51e90a69", + "text": "ВАВАВААВА", + "dateCreate": "2025-04-17T14:41:03.205Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6801133c6bc0478e51e90a7e", + "text": "АВВАВ", + "dateCreate": "2025-04-17T14:42:04.656Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680120257e843dad89603953", + "text": "aboba", + "dateCreate": "2025-04-17T15:37:09.719Z", + "author": { + "profile": { + "name": "AdminName" + }, + "_id": "67fb67dc8702ec3fc67fe37d" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6801298b7e843dad89603b69", + "text": "Ответ на первый коммент!", + "dateCreate": "2025-04-17T16:17:15.632Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68012d1f7e843dad89603dd2", + "text": "sdgsdg", + "dateCreate": "2025-04-17T16:32:31.493Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67ff6be2de177582e52338d7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680131b17e843dad89603fa8", + "text": "кто ты воин?)", + "dateCreate": "2025-04-17T16:52:01.634Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680112c36bc0478e51e90a4b", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6801363b7e843dad89604065", + "text": "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "dateCreate": "2025-04-17T17:11:23.192Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680139997e843dad89604174", + "text": "fdf", + "dateCreate": "2025-04-17T17:25:45.154Z", + "author": { + "profile": { + "name": "User №2" + }, + "_id": "67fb67dc8702ec3fc67fe380" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68013ed87e843dad896043a1", + "text": "5433222", + "dateCreate": "2025-04-17T17:48:08.522Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680112ff6bc0478e51e90a69", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6801754b7e843dad8960513f", + "text": "sdasdasd", + "dateCreate": "2025-04-17T21:40:27.549Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680008bc6bc0478e51e8ed26", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680175567e843dad89605142", + "text": "111111111111111111111", + "dateCreate": "2025-04-17T21:40:38.255Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680176847e843dad89605159", + "text": "хх", + "dateCreate": "2025-04-17T21:45:40.231Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680176f47e843dad89605167", + "text": "44", + "dateCreate": "2025-04-17T21:47:32.257Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680176847e843dad89605159", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "67fb67dc8702ec3fc67fe393", + "text": "123", + "dateCreate": "2025-04-17T22:22:28.538Z", + "author": { + "profile": { + "name": "User №21" + }, + "_id": "67fb67dc8702ec3fc67fe393" + }, + "parent": { + "_id": "680131b17e843dad89603fa8", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680199f97e843dad8960547c", + "text": "Ответ на 1001 коммент!", + "dateCreate": "2025-04-18T00:16:57.909Z", + "author": { + "profile": { + "name": "User №37" + }, + "_id": "67fb67dc8702ec3fc67fe3a3" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68019a0a7e843dad8960547e", + "text": "sdgsdg", + "dateCreate": "2025-04-18T00:17:14.310Z", + "author": { + "profile": { + "name": "User №37" + }, + "_id": "67fb67dc8702ec3fc67fe3a3" + }, + "parent": { + "_id": "67ff6be2de177582e52338d7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68019a137e843dad89605480", + "text": "1", + "dateCreate": "2025-04-18T00:17:23.658Z", + "author": { + "profile": { + "name": "User №37" + }, + "_id": "67fb67dc8702ec3fc67fe3a3" + }, + "parent": { + "_id": "680112c36bc0478e51e90a4b", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68019a5b7e843dad89605484", + "text": "Первый коммент!", + "dateCreate": "2025-04-18T00:18:35.243Z", + "author": { + "profile": { + "name": "User №37" + }, + "_id": "67fb67dc8702ec3fc67fe3a3" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6801ea457e843dad896057e3", + "text": "sdfsd", + "dateCreate": "2025-04-18T05:59:33.121Z", + "author": { + "profile": { + "name": "User №2" + }, + "_id": "67fb67dc8702ec3fc67fe380" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68021b6e7e843dad89605db1", + "text": "www", + "dateCreate": "2025-04-18T09:29:18.561Z", + "author": { + "profile": { + "name": "User №13" + }, + "_id": "67fb67dc8702ec3fc67fe38b" + }, + "parent": { + "_id": "6800f74a6bc0478e51e90312", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68021b817e843dad89605db4", + "text": "www1", + "dateCreate": "2025-04-18T09:29:37.410Z", + "author": { + "profile": { + "name": "User №13" + }, + "_id": "67fb67dc8702ec3fc67fe38b" + }, + "parent": { + "_id": "67ffcbc26bc0478e51e8e84a", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68021baf7e843dad89605db9", + "text": "222", + "dateCreate": "2025-04-18T09:30:23.687Z", + "author": { + "profile": { + "name": "User №13" + }, + "_id": "67fb67dc8702ec3fc67fe38b" + }, + "parent": { + "_id": "6800f3686bc0478e51e901bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "68021be27e843dad89605dbf", + "text": "1234567qwertyu cdwxcv4t5by5vt444444444444444444444444444444444444444\n\ndwcrvb5t4", + "dateCreate": "2025-04-18T09:31:14.490Z", + "author": { + "profile": { + "name": "User №13" + }, + "_id": "67fb67dc8702ec3fc67fe38b" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68021c317e843dad89605dc2", + "text": "1\n2\n#\n$", + "dateCreate": "2025-04-18T09:32:33.404Z", + "author": { + "profile": { + "name": "User №13" + }, + "_id": "67fb67dc8702ec3fc67fe38b" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6802333a7e843dad896060f0", + "text": "test_123", + "dateCreate": "2025-04-18T11:10:50.706Z", + "author": { + "profile": { + "name": "User №53" + }, + "_id": "67fb67dc8702ec3fc67fe3b3" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68024d8e7e843dad89606458", + "text": "z", + "dateCreate": "2025-04-18T13:03:10.931Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fee07ede177582e52335c5", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680271527e843dad89606dc1", + "text": "333", + "dateCreate": "2025-04-18T15:35:46.411Z", + "author": { + "profile": { + "name": "User №11" + }, + "_id": "67fb67dc8702ec3fc67fe389" + }, + "parent": { + "_id": "6800f3286bc0478e51e901ab", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680271607e843dad89606de9", + "text": "rtrtrt", + "dateCreate": "2025-04-18T15:36:00.421Z", + "author": { + "profile": { + "name": "User №11" + }, + "_id": "67fb67dc8702ec3fc67fe389" + }, + "parent": { + "_id": "6800f3686bc0478e51e901bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680271bd7e843dad89606e12", + "text": "1234567qwertyu cdwxcv4t5by5vt444444444444444444444444444444444444444 dwcrvb5t4 * 2", + "dateCreate": "2025-04-18T15:37:33.764Z", + "author": { + "profile": { + "name": "User №11" + }, + "_id": "67fb67dc8702ec3fc67fe389" + }, + "parent": { + "_id": "68021be27e843dad89605dbf", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680272357e843dad89606e2b", + "text": "teesssstttt", + "dateCreate": "2025-04-18T15:39:33.811Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6802723a7e843dad89606e35", + "text": "test", + "dateCreate": "2025-04-18T15:39:38.466Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "680272357e843dad89606e2b", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "680278b37e843dad89606fe7", + "text": "test", + "dateCreate": "2025-04-18T16:07:15.299Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "680278c97e843dad89606ff4", + "text": "test11", + "dateCreate": "2025-04-18T16:07:37.207Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027aec7e843dad896070f6", + "text": "test111", + "dateCreate": "2025-04-18T16:16:44.057Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027b357e843dad89607115", + "text": "test1111\n", + "dateCreate": "2025-04-18T16:17:57.615Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027b5e7e843dad89607127", + "text": "sdf", + "dateCreate": "2025-04-18T16:18:38.402Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027b7b7e843dad8960718b", + "text": "sdf", + "dateCreate": "2025-04-18T16:19:07.107Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027b807e843dad8960719d", + "text": "sdf", + "dateCreate": "2025-04-18T16:19:12.662Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027b867e843dad896071a0", + "text": "ytr", + "dateCreate": "2025-04-18T16:19:18.550Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027bba7e843dad896071b1", + "text": "vdfs", + "dateCreate": "2025-04-18T16:20:10.817Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027bc17e843dad896071b4", + "text": "dfg", + "dateCreate": "2025-04-18T16:20:17.117Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68027fe57e843dad89607355", + "text": " ", + "dateCreate": "2025-04-18T16:37:57.284Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68029a467e843dad896079e8", + "text": "", + "dateCreate": "2025-04-18T18:30:30.840Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68029a517e843dad896079eb", + "text": "", + "dateCreate": "2025-04-18T18:30:41.942Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "68029a707e843dad896079f4", + "text": "1", + "dateCreate": "2025-04-18T18:31:12.259Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6802bbe47e843dad89607ee7", + "text": "test", + "dateCreate": "2025-04-18T20:53:56.024Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "67fb67e98702ec3fc67fef7c", + "_type": "article" + }, + "isDeleted": false + }, + { + "_id": "6802bbe97e843dad89607eea", + "text": "test2", + "dateCreate": "2025-04-18T20:54:01.938Z", + "author": { + "profile": { + "name": "User №1" + }, + "_id": "67fb67dc8702ec3fc67fe37f" + }, + "parent": { + "_id": "6802bbe47e843dad89607ee7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6802dd407e843dad89608b66", + "text": "Комментарий 4 уровня", + "dateCreate": "2025-04-18T23:16:16.423Z", + "author": { + "profile": { + "name": "User №4" + }, + "_id": "67fb67dc8702ec3fc67fe382" + }, + "parent": { + "_id": "67ff6be2de177582e52338d7", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6802de647e843dad89608b95", + "text": "Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. Текст комментрия о том какой товар. Комментатор может оставить большой комментрий и он весь показывается. ", + "dateCreate": "2025-04-18T23:21:08.809Z", + "author": { + "profile": { + "name": "User №4" + }, + "_id": "67fb67dc8702ec3fc67fe382" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6802df0c7e843dad89608be1", + "text": "contentcontentcontentcontentcontentcontentcontentcontent", + "dateCreate": "2025-04-18T23:23:56.705Z", + "author": { + "profile": { + "name": "User №4" + }, + "_id": "67fb67dc8702ec3fc67fe382" + }, + "parent": { + "_id": "6802de647e843dad89608b95", + "_type": "comment" + }, + "isDeleted": false + }, + { + "_id": "6802df537e843dad89608c18", + "text": "2", + "dateCreate": "2025-04-18T23:25:07.318Z", + "author": { + "profile": { + "name": "User №4" + }, + "_id": "67fb67dc8702ec3fc67fe382" + }, + "parent": { + "_id": "67fee024de177582e52335bb", + "_type": "comment" + }, + "isDeleted": false + } + ], + "count": 113 +} diff --git a/src/app/article/index.js b/src/app/article/index.js index 54f037b64..accafba4d 100644 --- a/src/app/article/index.js +++ b/src/app/article/index.js @@ -13,7 +13,9 @@ import TopHead from '../../containers/top-head'; import { useDispatch, useSelector } from 'react-redux'; import shallowequal from 'shallowequal'; import articleActions from '../../store-redux/article/actions'; +import commentsActions from '../../store-redux/comments/actions'; import HeadLayout from '../../components/head-layout'; +import CommentsCard from '../../components/comments-card'; function Article() { const store = useStore(); @@ -26,16 +28,23 @@ function Article() { useInit(() => { //store.actions.article.load(params.id); dispatch(articleActions.load(params.id)); + dispatch(commentsActions.load(params.id)); }, [params.id]); const select = useSelector( state => ({ article: state.article.data, waiting: state.article.waiting, + comments: state.comments.comments, + count: state.comments.count, }), shallowequal, ); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект + console.log('------comments'); + console.log(select.comments); + console.log(select.count); + const { t } = useTranslate(); const callbacks = { @@ -55,6 +64,7 @@ function Article() { + diff --git a/src/components/comment/index.js b/src/components/comment/index.js new file mode 100644 index 000000000..8a1a91301 --- /dev/null +++ b/src/components/comment/index.js @@ -0,0 +1,55 @@ +import { memo } from 'react'; +import PropTypes from 'prop-types'; +import { cn as bem } from '@bem-react/classname'; +import numberFormat from '../../utils/number-format'; +import Button from '../button'; +import './style.css'; + +function Comment({ comment, comments, level = 0 }) { + // const { article, onAdd = () => {}, t = text => text } = props; + const cn = bem('Comment'); + + if (comment.isDeleted) return null; + + const children = comments.filter( + (c) => c.parent._id === comment._id && c.parent._type === "comment" && !c.isDeleted + ); + + return ( +
0 ? `ml-${level * 4}` : ''}`}> +
+
+ {comment.author.profile.name} + + {new Date(comment.dateCreate).toLocaleString()} + +
+

{comment.text}

+
+ {children.map((child) => ( + + ))} +
+ ); + +} + +// Comment.propTypes = { +// article: PropTypes.shape({ +// _id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +// description: PropTypes.string, +// madeIn: PropTypes.object, +// category: PropTypes.object, +// edition: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +// price: PropTypes.number, +// }).isRequired, +// onAdd: PropTypes.func, +// t: PropTypes.func, +// }; + +export default memo(Comment); diff --git a/src/components/comment/style.css b/src/components/comment/style.css new file mode 100644 index 000000000..41c94cd1b --- /dev/null +++ b/src/components/comment/style.css @@ -0,0 +1,35 @@ +.Comment { + display: flex; + flex-direction: column; + gap: 24px; + padding-block: 24px; +} + +.ArticleCard-prop-wrapper { + display: flex; + flex-direction: column; + gap: 12px; +} + +.ArticleCard-prop { + display: flex; + gap: 10px; +} + +.ArticleCard-prop_size_big { + font-family: var(--second-font-family); + font-size: 24px; + font-weight: bold; +} + +.ArticleCard-label { + width: 200px; +} + +.ArticleCard-value { + font-weight: bold; +} + +.ArticleCard-prop_size_big .ArticleCard-label { + width: fit-content; +} diff --git a/src/components/comments-card/index.js b/src/components/comments-card/index.js new file mode 100644 index 000000000..6251cc049 --- /dev/null +++ b/src/components/comments-card/index.js @@ -0,0 +1,77 @@ +import {memo, useCallback} from 'react'; +import PropTypes from 'prop-types'; +import { cn as bem } from '@bem-react/classname'; +import numberFormat from '../../utils/number-format'; +import Button from '../button'; +import './style.css'; +import useTranslate from '../../hooks/use-translate'; +import {useLocation, useNavigate} from 'react-router-dom'; +import useStore from '../../hooks/use-store'; +import useSelector from '../../hooks/use-selector'; +import Comment from '../comment'; + +function CommentsCard(props) { + // const { article, onAdd = () => {}, t = text => text } = props; + + const cn = bem('CommentsCard'); + const { t } = useTranslate(); + + const navigate = useNavigate(); + const location = useLocation(); + const store = useStore(); + + const select = useSelector(state => ({ + exists: state.session.exists, + })); + + const callbacks = { + // Переход к авторизации + onSignIn: useCallback(() => { + navigate('/login', { state: { back: location.pathname } }); + }, [location.pathname]), + + // Отмена авторизации + onSignOut: useCallback(() => { + store.actions.session.signOut(); + }, []), + }; + + const rootComments = props.comments.filter( + (comment) => comment.parent._type === "article" && !comment.isDeleted + ); + + return ( +
+
Комментарии ({props.count})
+
+ {rootComments.length > 0 && ( + rootComments.map((comment) => ( + ))) + } +
+ {!select.exists && (
+

) } +
+ ); +} + +// CommentsCard.propTypes = { +// article: PropTypes.shape({ +// _id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +// description: PropTypes.string, +// madeIn: PropTypes.object, +// category: PropTypes.object, +// edition: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +// price: PropTypes.number, +// }).isRequired, +// onAdd: PropTypes.func, +// t: PropTypes.func, +// }; + +export default memo(CommentsCard); diff --git a/src/components/comments-card/style.css b/src/components/comments-card/style.css new file mode 100644 index 000000000..8a6effcc3 --- /dev/null +++ b/src/components/comments-card/style.css @@ -0,0 +1,19 @@ +.CommentsCard { + display: flex; + flex-direction: column; + padding: 16px 0; + gap: 24px; +} + +.CommentsCard-description { + font-family: "Montserrat Alternates"; + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: 32px; +} + +.CommentsCard-login { + display: inline-block; +} + diff --git a/src/store-redux/comments/actions.js b/src/store-redux/comments/actions.js new file mode 100644 index 000000000..0f58a4656 --- /dev/null +++ b/src/store-redux/comments/actions.js @@ -0,0 +1,23 @@ +export default { + /** + * Загрузка товара + * @param id + * @return {Function} + */ + load: (id) => { + return async (dispatch, getState, services) => { + dispatch({ type: 'comments/load-start' }); + + try { + const res = await services.api.request({ + url: `/api/v1/comments?fields=items(_id,text,dateCreate,author(profile(name)),parent(_id,_type),isDeleted),count&limit=*&search[parent]=${id}`, + }); + // Товар загружен успешно + dispatch({ type: 'comments/load-success', payload: { data: res.data.result } }); + } catch (e) { + //Ошибка загрузки + dispatch({ type: 'comments/load-error' }); + } + }; + }, +}; diff --git a/src/store-redux/comments/reducer.js b/src/store-redux/comments/reducer.js new file mode 100644 index 000000000..3a205ea55 --- /dev/null +++ b/src/store-redux/comments/reducer.js @@ -0,0 +1,26 @@ +// Начальное состояние +export const initialState = { + comments: [], + count: 0, + waiting: false, // признак ожидания загрузки +}; + +// Обработчик действий +function reducer(state = initialState, action) { + switch (action.type) { + case 'comments/load-start': + return { ...state, comments: [], count: 0, waiting: true }; + + case 'comments/load-success': + return { ...state, comments: action.payload.data.items, count: action.payload.data.count, waiting: false }; + + case 'comments/load-error': + return { ...state, comments: [], count: 0, waiting: false }; //@todo текст ошибки сохранять? + + default: + // Нет изменений + return state; + } +} + +export default reducer; diff --git a/src/store-redux/exports.js b/src/store-redux/exports.js index 1a0a3d742..c7f1c588b 100644 --- a/src/store-redux/exports.js +++ b/src/store-redux/exports.js @@ -1,2 +1,3 @@ export { default as article } from './article/reducer'; export { default as modals } from './modals/reducer'; +export { default as comments } from './comments/reducer'; From 254020712531d4352bb0682ead17a05e481ddc39 Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Sun, 20 Apr 2025 04:03:46 +0300 Subject: [PATCH 02/10] =?UTF-8?q?lecture-5=20=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=B5=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/index.js | 2 +- src/components/button/style.css | 27 ++++++++++ src/components/comment/index.js | 44 +++++++++++----- src/components/comment/style.css | 51 ++++++++++-------- src/components/comments-card/index.js | 12 +++-- src/components/comments-card/style.css | 3 +- src/components/reply-form/index.js | 72 ++++++++++++++++++++++++++ src/components/reply-form/style.css | 24 +++++++++ 8 files changed, 196 insertions(+), 39 deletions(-) create mode 100644 src/components/reply-form/index.js create mode 100644 src/components/reply-form/style.css diff --git a/src/components/button/index.js b/src/components/button/index.js index 7d1d3d05c..0637a68cc 100644 --- a/src/components/button/index.js +++ b/src/components/button/index.js @@ -16,7 +16,7 @@ function Button({ onClick = () => {}, title, style, type = 'button' }) { Button.propTypes = { onClick: PropTypes.func, title: PropTypes.string, - style: PropTypes.oneOf(['text', 'primary', 'delete', 'outline']), + style: PropTypes.oneOf(['text', 'primary', 'delete', 'outline', 'login', 'comment']), type: PropTypes.oneOf(['button', 'submit']), }; diff --git a/src/components/button/style.css b/src/components/button/style.css index 0efc8a896..4caaa02dc 100644 --- a/src/components/button/style.css +++ b/src/components/button/style.css @@ -39,3 +39,30 @@ color: var(--primary); } } + +.Button_style_comment { + padding: 0; + border: none; + font-size: 12px; + color: var(--primary); + + &:hover { + color: var(--primary); + } +} + +.Button_style_login { + display: inline-block; + padding: 0; + border: none; + font-family: "Golos Text"; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 22px; + color: var(--primary); + + &:hover { + color: var(--primary); + } +} diff --git a/src/components/comment/index.js b/src/components/comment/index.js index 8a1a91301..4403abf87 100644 --- a/src/components/comment/index.js +++ b/src/components/comment/index.js @@ -1,42 +1,60 @@ -import { memo } from 'react'; +import { memo, useCallback, useState } from 'react'; import PropTypes from 'prop-types'; import { cn as bem } from '@bem-react/classname'; -import numberFormat from '../../utils/number-format'; import Button from '../button'; import './style.css'; +import ReplyForm from '../reply-form'; -function Comment({ comment, comments, level = 0 }) { +function Comment({ comment, comments, level = 0, replyToCommentId, setReplyToCommentId }) { // const { article, onAdd = () => {}, t = text => text } = props; const cn = bem('Comment'); + const [isReply, setIsReply] = useState(false); if (comment.isDeleted) return null; const children = comments.filter( - (c) => c.parent._id === comment._id && c.parent._type === "comment" && !c.isDeleted + item => item.parent._id === comment._id && item.parent._type === 'comment' && !item.isDeleted, ); + const callbacks = { + onReply: useCallback(() => { + setReplyToCommentId(comment._id); + }, [comment._id, setReplyToCommentId]), + + onReplyChancel: useCallback(() => { + setReplyToCommentId(null); + }, [setReplyToCommentId]), + }; + return ( -
0 ? `ml-${level * 4}` : ''}`}> -
+
0 })}> +
- {comment.author.profile.name} - - {new Date(comment.dateCreate).toLocaleString()} - + {comment.author.profile.name} + {new Date(comment.dateCreate).toLocaleString()}
-

{comment.text}

+

{comment.text}

+
- {children.map((child) => ( + {children.map(child => ( ))} + {replyToCommentId === comment._id && ( + + )}
); - } // Comment.propTypes = { diff --git a/src/components/comment/style.css b/src/components/comment/style.css index 41c94cd1b..0c4509ef5 100644 --- a/src/components/comment/style.css +++ b/src/components/comment/style.css @@ -1,35 +1,44 @@ .Comment { - display: flex; - flex-direction: column; - gap: 24px; - padding-block: 24px; } -.ArticleCard-prop-wrapper { +.Comment-content { display: flex; flex-direction: column; - gap: 12px; -} - -.ArticleCard-prop { - display: flex; - gap: 10px; + gap: 6px; } -.ArticleCard-prop_size_big { - font-family: var(--second-font-family); - font-size: 24px; - font-weight: bold; +.Comment-wrapper_nested { + margin-left: 40px; } -.ArticleCard-label { - width: 200px; +.Comment-name { + margin: 0; + padding: 0; + font-family: "Golos Text"; + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: 18px; + color: #4B5563; } -.ArticleCard-value { - font-weight: bold; +.Comment-date { + margin: 0; + padding: 0; + font-family: "Golos Text"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; + color: #666; } -.ArticleCard-prop_size_big .ArticleCard-label { - width: fit-content; +.Comment-text { + margin: 0; + padding: 0; + font-family: "Golos Text"; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; } diff --git a/src/components/comments-card/index.js b/src/components/comments-card/index.js index 6251cc049..df221afbe 100644 --- a/src/components/comments-card/index.js +++ b/src/components/comments-card/index.js @@ -1,4 +1,4 @@ -import {memo, useCallback} from 'react'; +import {memo, useCallback, useState} from 'react'; import PropTypes from 'prop-types'; import { cn as bem } from '@bem-react/classname'; import numberFormat from '../../utils/number-format'; @@ -9,6 +9,7 @@ import {useLocation, useNavigate} from 'react-router-dom'; import useStore from '../../hooks/use-store'; import useSelector from '../../hooks/use-selector'; import Comment from '../comment'; +import ReplyForm from '../reply-form'; function CommentsCard(props) { // const { article, onAdd = () => {}, t = text => text } = props; @@ -35,6 +36,7 @@ function CommentsCard(props) { store.actions.session.signOut(); }, []), }; + const [replyToCommentId, setReplyToCommentId] = useState(null); const rootComments = props.comments.filter( (comment) => comment.parent._type === "article" && !comment.isDeleted @@ -51,12 +53,16 @@ function CommentsCard(props) { comment={comment} comments={props.comments} level={0} + replyToCommentId={replyToCommentId} + setReplyToCommentId={setReplyToCommentId} /> ))) }
{!select.exists && (
-

) } +
)} + {select.exists && !replyToCommentId && {}}/>} ); } diff --git a/src/components/comments-card/style.css b/src/components/comments-card/style.css index 8a6effcc3..6c4cedb58 100644 --- a/src/components/comments-card/style.css +++ b/src/components/comments-card/style.css @@ -14,6 +14,7 @@ } .CommentsCard-login { - display: inline-block; + display: flex; + align-items: center; } diff --git a/src/components/reply-form/index.js b/src/components/reply-form/index.js new file mode 100644 index 000000000..1b73a3faf --- /dev/null +++ b/src/components/reply-form/index.js @@ -0,0 +1,72 @@ +import {memo, useCallback, useState} from 'react'; +import useTranslate from '../../hooks/use-translate'; +import {useLocation, useNavigate} from 'react-router-dom'; +import useStore from '../../hooks/use-store'; +import useSelector from '../../hooks/use-selector'; +import useInit from '../../hooks/use-init'; +import Button from '../button'; +import {cn as bem} from '@bem-react/classname'; +import './style.css'; + +function ReplyForm(props) { + const {t} = useTranslate(); + const location = useLocation(); + const navigate = useNavigate(); + const store = useStore(); + + const cn = bem('ReplyForm'); + + useInit(() => { + // store.actions.session.resetErrors(); + }); + + const select = useSelector(state => ({ + waiting: state.session.waiting, + errors: state.session.errors, + })); + + const [data, setData] = useState({ + // login: '', + // password: '', + text: '', + _id: props._id + }); + + const callbacks = { + // Колбэк на ввод в элементах формы + onChange: useCallback((value, name) => { + setData(prevData => ({...prevData, [name]: value})); + }, []), + + // Отправка данных формы для авторизации + onSubmit: useCallback( + e => { + e.preventDefault(); + store.actions.session.signIn(data, () => { + // Возврат на страницу, с которой пришли + const back = + location.state?.back && location.state?.back !== location.pathname + ? location.state?.back + : '/'; + navigate(back); + }); + }, + [data, location.state], + ), + }; + + return ( + <> +
+

{props.title}

+ +
+
+
+ + ); +} + +export default memo(ReplyForm); diff --git a/src/components/reply-form/style.css b/src/components/reply-form/style.css new file mode 100644 index 000000000..ac75089c3 --- /dev/null +++ b/src/components/reply-form/style.css @@ -0,0 +1,24 @@ +.ReplyForm { + display: flex; + flex-direction: column; + row-gap: 16px; +} + +.ReplyForm-title { + margin: 0; + padding: 0; + font-family: "Golos Text"; + font-size: 16px; + font-style: normal; + font-weight: 700; + line-height: 22px; +} + +.ReplyForm-textarea { + height: 88px; +} + +.ReplyForm-footer { + display: flex; + column-gap: 16px; +} From f10e2c2f6c2dd381752d46dd118c06da48870b5e Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Sun, 20 Apr 2025 12:28:07 +0300 Subject: [PATCH 03/10] =?UTF-8?q?lecture-5=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D0=B8=D0=B5=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/article/index.js | 6 +-- src/components/comment/index.js | 23 +++++++++-- src/components/comment/style.css | 3 ++ src/components/comments-card/index.js | 56 +++++++++++++++++--------- src/components/comments-card/style.css | 9 ++++- src/components/reply-form/index.js | 22 ++++------ src/components/reply-form/style.css | 1 + src/store-redux/comments/actions.js | 21 ++++++++++ src/store-redux/comments/reducer.js | 14 +++++++ 9 files changed, 111 insertions(+), 44 deletions(-) diff --git a/src/app/article/index.js b/src/app/article/index.js index accafba4d..e0bd25993 100644 --- a/src/app/article/index.js +++ b/src/app/article/index.js @@ -41,10 +41,6 @@ function Article() { shallowequal, ); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект - console.log('------comments'); - console.log(select.comments); - console.log(select.count); - const { t } = useTranslate(); const callbacks = { @@ -64,7 +60,7 @@ function Article() { - + diff --git a/src/components/comment/index.js b/src/components/comment/index.js index 4403abf87..ba8d5d9fa 100644 --- a/src/components/comment/index.js +++ b/src/components/comment/index.js @@ -5,10 +5,18 @@ import Button from '../button'; import './style.css'; import ReplyForm from '../reply-form'; -function Comment({ comment, comments, level = 0, replyToCommentId, setReplyToCommentId }) { +function Comment({ + comment, + comments, + level = 0, + isLogin, + loginComment, + replyToCommentId, + setReplyToCommentId, + onSendReply, +}) { // const { article, onAdd = () => {}, t = text => text } = props; const cn = bem('Comment'); - const [isReply, setIsReply] = useState(false); if (comment.isDeleted) return null; @@ -27,7 +35,7 @@ function Comment({ comment, comments, level = 0, replyToCommentId, setReplyToCom }; return ( -
0 })}> +
0 && level < 10 })}>
{comment.author.profile.name} @@ -42,17 +50,24 @@ function Comment({ comment, comments, level = 0, replyToCommentId, setReplyToCom comment={child} comments={comments} level={level + 1} + isLogin={isLogin} + loginComment={loginComment} replyToCommentId={replyToCommentId} setReplyToCommentId={setReplyToCommentId} + onSendReply={onSendReply} /> ))} - {replyToCommentId === comment._id && ( + {replyToCommentId === comment._id && isLogin && ( )} + {replyToCommentId === comment._id && !isLogin && loginComment}
); } diff --git a/src/components/comment/style.css b/src/components/comment/style.css index 0c4509ef5..55a39e3c5 100644 --- a/src/components/comment/style.css +++ b/src/components/comment/style.css @@ -36,6 +36,9 @@ .Comment-text { margin: 0; padding: 0; + max-width: 100%; + word-wrap: break-word; + overflow-wrap: break-word; font-family: "Golos Text"; font-size: 14px; font-style: normal; diff --git a/src/components/comments-card/index.js b/src/components/comments-card/index.js index df221afbe..f13f59b71 100644 --- a/src/components/comments-card/index.js +++ b/src/components/comments-card/index.js @@ -1,21 +1,24 @@ -import {memo, useCallback, useState} from 'react'; +import { memo, useCallback, useState } from 'react'; import PropTypes from 'prop-types'; import { cn as bem } from '@bem-react/classname'; import numberFormat from '../../utils/number-format'; import Button from '../button'; import './style.css'; import useTranslate from '../../hooks/use-translate'; -import {useLocation, useNavigate} from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import useStore from '../../hooks/use-store'; import useSelector from '../../hooks/use-selector'; import Comment from '../comment'; import ReplyForm from '../reply-form'; +import commentsActions from '../../store-redux/comments/actions'; +import { useDispatch } from 'react-redux'; function CommentsCard(props) { // const { article, onAdd = () => {}, t = text => text } = props; const cn = bem('CommentsCard'); const { t } = useTranslate(); + const dispatch = useDispatch(); const navigate = useNavigate(); const location = useLocation(); @@ -25,44 +28,59 @@ function CommentsCard(props) { exists: state.session.exists, })); + const [replyToCommentId, setReplyToCommentId] = useState(null); + + const rootComments = props.comments.filter( + comment => comment.parent._type === 'article' && !comment.isDeleted, + ); + const callbacks = { // Переход к авторизации onSignIn: useCallback(() => { navigate('/login', { state: { back: location.pathname } }); }, [location.pathname]), - // Отмена авторизации - onSignOut: useCallback(() => { - store.actions.session.signOut(); - }, []), + // Отправка комментария + onSendReply: useCallback(data => dispatch(commentsActions.reply(data)), [replyToCommentId]), }; - const [replyToCommentId, setReplyToCommentId] = useState(null); - const rootComments = props.comments.filter( - (comment) => comment.parent._type === "article" && !comment.isDeleted + const loginComment = ( +
+
); return (
-
Комментарии ({props.count})
+
Комментарии ({props.count})
- {rootComments.length > 0 && ( - rootComments.map((comment) => ( + {rootComments.length > 0 && + rootComments.map(comment => ( ))) - } + onSendReply={callbacks.onSendReply} + /> + ))}
- {!select.exists && (
-
)} - {select.exists && !replyToCommentId && {}}/>} + {!select.exists && loginComment} + {select.exists && !replyToCommentId && ( + {}} + type={'article'} + _id={props.article._id} + onSendReply={callbacks.onSendReply} + /> + )}
); } diff --git a/src/components/comments-card/style.css b/src/components/comments-card/style.css index 6c4cedb58..f8e3db9c4 100644 --- a/src/components/comments-card/style.css +++ b/src/components/comments-card/style.css @@ -5,7 +5,13 @@ gap: 24px; } -.CommentsCard-description { +.CommentsCard-comments { + display: flex; + flex-direction: column; + row-gap: 16px; +} + +.CommentsCard-title { font-family: "Montserrat Alternates"; font-size: 24px; font-style: normal; @@ -14,6 +20,7 @@ } .CommentsCard-login { + margin: 16px 0; display: flex; align-items: center; } diff --git a/src/components/reply-form/index.js b/src/components/reply-form/index.js index 1b73a3faf..4d1e95a16 100644 --- a/src/components/reply-form/index.js +++ b/src/components/reply-form/index.js @@ -17,7 +17,6 @@ function ReplyForm(props) { const cn = bem('ReplyForm'); useInit(() => { - // store.actions.session.resetErrors(); }); const select = useSelector(state => ({ @@ -26,32 +25,25 @@ function ReplyForm(props) { })); const [data, setData] = useState({ - // login: '', - // password: '', text: '', - _id: props._id + parent: { _id: props._id, _type: props.type } }); const callbacks = { // Колбэк на ввод в элементах формы onChange: useCallback((value, name) => { - setData(prevData => ({...prevData, [name]: value})); + setData(prevData => ({ ...prevData, [name]: value })); }, []), // Отправка данных формы для авторизации onSubmit: useCallback( e => { e.preventDefault(); - store.actions.session.signIn(data, () => { - // Возврат на страницу, с которой пришли - const back = - location.state?.back && location.state?.back !== location.pathname - ? location.state?.back - : '/'; - navigate(back); - }); + props.onSendReply(data); + setData(prevData => ({ ...prevData, text: '' })); + props.onChancel(); }, - [data, location.state], + [data, props.onSendReply, props.onChancel], ), }; @@ -59,7 +51,7 @@ function ReplyForm(props) { <>

{props.title}

- +
); } +ReplyForm.propTypes = { + title: PropTypes.string, + placeholder: PropTypes.string, + onChancel: PropTypes.func, + onSendReply: PropTypes.func, + type: PropTypes.string, + _id: PropTypes.string, +}; + export default memo(ReplyForm); From cb25d7bea1a1373d95ccaa283ec3d3b4da367db3 Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Mon, 21 Apr 2025 07:18:39 +0300 Subject: [PATCH 06/10] lecture-5 I18nService --- src/app/article/index.js | 5 ++--- src/app/main/index.js | 5 ++--- src/app/profile/index.js | 3 ++- src/config.js | 3 +++ src/hooks/use-translate.js | 28 ++++++++++++++++++++++++++-- src/i18n/index.js | 34 ++++++++++++++++++++++++++++++++++ src/index.js | 9 +++------ src/services.js | 12 ++++++++++++ 8 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 src/i18n/index.js diff --git a/src/app/article/index.js b/src/app/article/index.js index dbd7bc51c..bd336887a 100644 --- a/src/app/article/index.js +++ b/src/app/article/index.js @@ -19,6 +19,7 @@ import CommentsCard from '../../components/comments-card'; function Article() { const store = useStore(); + const { t, lang } = useTranslate(); const dispatch = useDispatch(); // Параметры из пути /articles/:id @@ -29,7 +30,7 @@ function Article() { //store.actions.article.load(params.id); dispatch(articleActions.load(params.id)); dispatch(commentsActions.load(params.id)); - }, [params.id]); + }, [params.id, lang]); const select = useSelector( state => ({ @@ -42,8 +43,6 @@ function Article() { shallowequal, ); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект - const { t } = useTranslate(); - const callbacks = { // Добавление в корзину addToBasket: useCallback(_id => store.actions.basket.addToBasket(_id), [store]), diff --git a/src/app/main/index.js b/src/app/main/index.js index d9fb1c6c2..a160e0a03 100644 --- a/src/app/main/index.js +++ b/src/app/main/index.js @@ -13,17 +13,16 @@ import HeadLayout from '../../components/head-layout'; function Main() { const store = useStore(); + const { t, lang } = useTranslate(); useInit( async () => { await Promise.all([store.actions.catalog.initParams(), store.actions.categories.load()]); }, - [], + [lang], true, ); - const { t } = useTranslate(); - return ( <> diff --git a/src/app/profile/index.js b/src/app/profile/index.js index 166d127fb..3b741323f 100644 --- a/src/app/profile/index.js +++ b/src/app/profile/index.js @@ -25,7 +25,8 @@ function Profile() { })); const { t } = useTranslate(); - + console.log('Profile translate----'); + console.log(t('title')); return ( <> diff --git a/src/config.js b/src/config.js index 67c72f734..68d525fe8 100644 --- a/src/config.js +++ b/src/config.js @@ -18,6 +18,9 @@ const config = { api: { baseUrl: '', }, + i18n: { + baseLang: 'ru', + }, }; export default config; diff --git a/src/hooks/use-translate.js b/src/hooks/use-translate.js index bdcbf1071..a9bcf507b 100644 --- a/src/hooks/use-translate.js +++ b/src/hooks/use-translate.js @@ -1,9 +1,33 @@ -import { useCallback, useContext } from 'react'; +import {useCallback, useContext, useMemo, useState, useEffect} from 'react'; import { I18nContext } from '../i18n/context'; +import translate from '../i18n/translate'; +import useServices from './use-services'; /** * Хук возвращает функцию для локализации текстов, код языка и функцию его смены */ export default function useTranslate() { - return useContext(I18nContext); + const i18nService = useServices().i18n; + + const [langState, setLangState] = useState(i18nService.getLang()); + + useEffect(() => { + return i18nService.subscribe(setLangState); + }, [i18nService]); + + const i18n = useMemo( + () => ({ + // Код локали + lang: langState, + // Функция для смены локали + setLang: (lang) => { + i18nService.setLang(lang); + }, + // Функция для локализации текстов с замыканием на код языка + t: (text, number) => translate(langState, text, number), + }), + [langState, i18nService], + ); + + return i18n; } diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100644 index 000000000..c5584acd1 --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,34 @@ +class I18nService { + /** + * @param services {Services} Менеджер сервисов + * @param config {Object} + */ + constructor(services, config = {}) { + this.services = services; + this.config = config; + this.lang = config.baseLang; + this.listeners = new Set(); + this.services.api.setHeader('Accept-Language', this.lang); + } + + getLang() { + return this.lang; + } + + setLang(lang) { + this.lang = lang; + this.services.api.setHeader('Accept-Language', lang); + this.notifyListeners(); + } + + subscribe(listener) { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + notifyListeners() { + this.listeners.forEach(listener => listener(this.lang)); + } +} + +export default I18nService; diff --git a/src/index.js b/src/index.js index f49db385a..9404d09eb 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,6 @@ import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import { ServicesContext } from './context'; -import { I18nProvider } from './i18n/context'; import App from './app'; import Services from './services'; import config from './config'; @@ -16,11 +15,9 @@ const root = createRoot(document.getElementById('root')); root.render( - - - - - + + + , ); diff --git a/src/services.js b/src/services.js index a32b14d57..8ab16cc89 100644 --- a/src/services.js +++ b/src/services.js @@ -1,6 +1,7 @@ import APIService from './api'; import Store from './store'; import createStoreRedux from './store-redux'; +import I18nService from './i18n'; class Services { constructor(config) { @@ -38,6 +39,17 @@ class Services { } return this._redux; } + + /** + * Сервис I18n + * @returns {I18nService} + */ + get i18n() { + if (!this._i18n) { + this._i18n = new I18nService(this, this.config.i18n); + } + return this._i18n; + } } export default Services; From d4acf357b418d0db3c0800c62ffcb6557a9684d7 Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Mon, 21 Apr 2025 17:24:48 +0300 Subject: [PATCH 07/10] =?UTF-8?q?lecture-5=20=D0=A4=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D0=B0=20-=20=D0=BE?= =?UTF-8?q?=D1=82=D1=81=D1=82=D1=83=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/reply-form/index.js | 2 +- src/components/reply-form/style.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/reply-form/index.js b/src/components/reply-form/index.js index 20548d193..a4c7e79b6 100644 --- a/src/components/reply-form/index.js +++ b/src/components/reply-form/index.js @@ -49,7 +49,7 @@ function ReplyForm(props) { return ( <> -
+

{props.title}

diff --git a/src/components/reply-form/style.css b/src/components/reply-form/style.css index 31af865c1..43f454f04 100644 --- a/src/components/reply-form/style.css +++ b/src/components/reply-form/style.css @@ -5,7 +5,7 @@ row-gap: 16px; } -.ReplyForm-comment { +.ReplyForm_comment { margin-left: 40px; } From 2c47aec05f38d2a40c48be660ed01670f6f33987 Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Tue, 22 Apr 2025 08:19:12 +0300 Subject: [PATCH 09/10] =?UTF-8?q?lecture-5=20=D0=98=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/comment/index.js | 1 - src/components/comments-card/index.js | 20 ++++++++++++++++++-- src/components/reply-form/index.js | 2 ++ src/components/reply-form/style.css | 7 ++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/comment/index.js b/src/components/comment/index.js index 72924f8e5..1efe014a8 100644 --- a/src/components/comment/index.js +++ b/src/components/comment/index.js @@ -48,7 +48,6 @@ function Comment({ behavior: 'smooth', }); } - }, [replyToCommentId]); return ( diff --git a/src/components/comments-card/index.js b/src/components/comments-card/index.js index d31664aa4..7f8381941 100644 --- a/src/components/comments-card/index.js +++ b/src/components/comments-card/index.js @@ -1,4 +1,4 @@ -import { memo, useCallback, useState } from 'react'; +import {memo, useCallback, useEffect, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import { cn as bem } from '@bem-react/classname'; import numberFormat from '../../utils/number-format'; @@ -15,6 +15,7 @@ import { useDispatch } from 'react-redux'; function CommentsCard(props) { // const { article, onAdd = () => {}, t = text => text } = props; + const replyLoginRef = useRef(null); const cn = bem('CommentsCard'); const { t } = useTranslate(); @@ -45,12 +46,27 @@ function CommentsCard(props) { }; const loginComment = ( -
+
); + useEffect(() => { + if (replyToCommentId && replyLoginRef.current) { + const rect = replyLoginRef.current.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const scrollY = window.scrollY; + + const scrollToPosition = scrollY + rect.top - windowHeight + rect.height + 250; + + window.scrollTo({ + top: scrollToPosition, + behavior: 'smooth', + }); + } + }, [replyToCommentId]); + return (
Комментарии ({props.count})
diff --git a/src/components/reply-form/index.js b/src/components/reply-form/index.js index 5be01fd81..10939ee46 100644 --- a/src/components/reply-form/index.js +++ b/src/components/reply-form/index.js @@ -39,9 +39,11 @@ function ReplyForm(props) { onSubmit: useCallback( e => { e.preventDefault(); + if (data.text.trim() !== '') { props.onSendReply(data); setData(prevData => ({ ...prevData, text: '' })); props.onChancel(); + } }, [data, props.onSendReply, props.onChancel], ), diff --git a/src/components/reply-form/style.css b/src/components/reply-form/style.css index 43f454f04..fc58b19c0 100644 --- a/src/components/reply-form/style.css +++ b/src/components/reply-form/style.css @@ -20,10 +20,15 @@ } .ReplyForm-textarea { + padding: 8px 12px; height: 88px; + border: 1px solid var(--filter-border); + border-radius: 4px; } .ReplyForm-footer { display: flex; - column-gap: 16px; + flex-wrap: wrap; + align-items: flex-start; + gap: 16px; } From 0dbed2fb070095680d0ce96c2a2d54d7f7927618 Mon Sep 17 00:00:00 2001 From: TolkinSL Date: Tue, 22 Apr 2025 13:29:44 +0300 Subject: [PATCH 10/10] =?UTF-8?q?lecture-5=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B8=20=D0=BD=D0=B0=20lis?= =?UTF-8?q?tToTree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/comment/index.js | 16 ++++++++++------ src/components/comments-card/index.js | 14 ++++++++++---- src/utils/list-to-tree/index.js | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/components/comment/index.js b/src/components/comment/index.js index 1efe014a8..dac3a23fb 100644 --- a/src/components/comment/index.js +++ b/src/components/comment/index.js @@ -21,9 +21,13 @@ function Comment({ if (comment.isDeleted) return null; - const children = comments.filter( - item => item.parent._id === comment._id && item.parent._type === 'comment' && !item.isDeleted, - ); + // const children = comments.filter( + // item => item.parent._id === comment._id && item.parent._type === 'comment' && !item.isDeleted, + // ); + const children = comment?.children; + + // console.log('Comment children ----'); + // console.log(children); const callbacks = { onReply: useCallback(() => { @@ -54,17 +58,17 @@ function Comment({
0 && level < 10 })}>
- {comment.author.profile.name} + {comment?.author?.profile.name} {new Date(comment.dateCreate).toLocaleString()}

{comment.text}

- {children.map(child => ( + {children?.map(child => ( {}, t = text => text } = props; @@ -31,9 +32,14 @@ function CommentsCard(props) { const [replyToCommentId, setReplyToCommentId] = useState(null); - const rootComments = props.comments.filter( - comment => comment.parent._type === 'article' && !comment.isDeleted, - ); + // const rootComments = props.comments.filter( + // comment => comment.parent._type === 'article' && !comment.isDeleted, + // ); + + const rootComments = listToTree(props.comments); + + // console.log('Comment Root ----'); + // console.log(rootComments); const callbacks = { // Переход к авторизации @@ -76,7 +82,7 @@ function CommentsCard(props) {