@coopenomics/desktop 2025.11.24-alpha-2 → 2025.11.24-alpha-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensions/{coopgram → chatcoop}/README.md +12 -12
- package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/api/index.ts +4 -4
- package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/store.ts +10 -10
- package/extensions/chatcoop/entities/ChatCoopChat/model/types.ts +3 -0
- package/extensions/chatcoop/entities/index.ts +1 -0
- package/extensions/{coopgram → chatcoop}/install.ts +22 -10
- package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/model/useCreateMatrixAccount.ts +2 -2
- package/extensions/{coopgram/pages/CoopgramPage/ui/CoopgramPage.vue → chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue} +17 -16
- package/extensions/chatcoop/pages/ChatCoopPage/ui/index.ts +1 -0
- package/extensions/chatcoop/pages/MobileClientPage/index.ts +1 -0
- package/extensions/chatcoop/pages/MobileClientPage/ui/MobileClientPage.vue +389 -0
- package/extensions/chatcoop/pages/index.ts +2 -0
- package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/model/useMatrixRegistration.ts +2 -2
- package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/ui/MatrixRegistration.vue +1 -1
- package/package.json +6 -6
- package/src/pages/ExtensionStore/ExtensionPage/ExtensionPage.vue +2 -5
- package/src/shared/lib/utils/index.ts +1 -0
- package/src/shared/lib/utils/schema.ts +20 -0
- package/src/widgets/ExtensionInstall/ui/ExtensionInstall.vue +2 -5
- package/src/widgets/ExtensionSettings/ui/ExtensionSettings.vue +2 -5
- package/extensions/coopgram/entities/CoopgramChat/model/types.ts +0 -3
- package/extensions/coopgram/entities/index.ts +0 -1
- package/extensions/coopgram/pages/CoopgramPage/ui/index.ts +0 -1
- package/extensions/coopgram/pages/index.ts +0 -1
- /package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/index.ts +0 -0
- /package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/index.ts +0 -0
- /package/extensions/{coopgram → chatcoop}/index.ts +0 -0
- /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/index.ts +0 -0
- /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/ui/CreateMatrixAccountButton.vue +0 -0
- /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/index.ts +0 -0
- /package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/index.ts +0 -0
- /package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/model/types.ts +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ChatCoop Extension
|
|
2
2
|
|
|
3
3
|
Расширение для интеграции Matrix чата в кооперативную систему.
|
|
4
4
|
|
|
5
5
|
## Описание
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
ChatCoop предоставляет встроенный Matrix чат для пользователей кооператива. Расширение:
|
|
8
8
|
|
|
9
9
|
- Получает временный токен аутентификации через GraphQL API
|
|
10
10
|
- Отображает Matrix клиент (Element Web) в iframe
|
|
@@ -13,14 +13,14 @@ Coopgram предоставляет встроенный Matrix чат для п
|
|
|
13
13
|
## Структура
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
|
|
16
|
+
chatcoop/
|
|
17
17
|
├── install.ts # Конфигурация рабочего стола
|
|
18
18
|
├── entities/
|
|
19
|
-
│ └──
|
|
19
|
+
│ └── ChatCoopChat/ # Entity для работы с Matrix токенами
|
|
20
20
|
│ ├── api/ # GraphQL запросы через SDK
|
|
21
21
|
│ └── model/ # Store и типы данных из SDK
|
|
22
22
|
├── pages/
|
|
23
|
-
│ └──
|
|
23
|
+
│ └── ChatCoopPage/ # Главная страница с iframe
|
|
24
24
|
├── shared/ # Общие компоненты
|
|
25
25
|
└── widgets/ # Виджеты
|
|
26
26
|
```
|
|
@@ -29,14 +29,14 @@ coopgram/
|
|
|
29
29
|
|
|
30
30
|
Расширение использует типизированные запросы из SDK:
|
|
31
31
|
|
|
32
|
-
- **Query**: `Queries.
|
|
33
|
-
- **Selector**: `
|
|
32
|
+
- **Query**: `Queries.ChatCoop.GetToken`
|
|
33
|
+
- **Selector**: `chatcoopTokenSelector`
|
|
34
34
|
- **Типы**: Автоматически генерируются из GraphQL схемы
|
|
35
35
|
|
|
36
36
|
## Работа с iframe URL
|
|
37
37
|
|
|
38
|
-
1. При загрузке страницы вызывается `
|
|
39
|
-
2. Entity отправляет типизированный GraphQL запрос через SDK: `Queries.
|
|
38
|
+
1. При загрузке страницы вызывается `chatcoopStore.loadToken()`
|
|
39
|
+
2. Entity отправляет типизированный GraphQL запрос через SDK: `Queries.ChatCoop.GetToken`
|
|
40
40
|
3. Бэкенд проверяет/создает Matrix пользователя и генерирует токен
|
|
41
41
|
4. Бэкенд формирует полную iframe URL с токеном аутентификации
|
|
42
42
|
5. Store сохраняет полученную ссылку в состоянии (типизировано через SDK)
|
|
@@ -46,15 +46,15 @@ coopgram/
|
|
|
46
46
|
## Конфигурация
|
|
47
47
|
|
|
48
48
|
- **MATRIX_CLIENT_URL**: URL Element Web клиента
|
|
49
|
-
- **workspace**: '
|
|
49
|
+
- **workspace**: 'chatcoop'
|
|
50
50
|
- **defaultRoute**: 'chat'
|
|
51
51
|
|
|
52
52
|
## Архитектура
|
|
53
53
|
|
|
54
54
|
Расширение построено по принципам Feature-Sliced Design (FSD):
|
|
55
55
|
|
|
56
|
-
- **Entities**: `
|
|
57
|
-
- **Pages**: `
|
|
56
|
+
- **Entities**: `ChatCoopChat` - бизнес-сущность для работы с Matrix токенами
|
|
57
|
+
- **Pages**: `ChatCoopPage` - UI страница без бизнес-логики
|
|
58
58
|
- **Store**: Pinia store для управления состоянием токена
|
|
59
59
|
- **API**: Функции для GraphQL запросов
|
|
60
60
|
|
package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/api/index.ts
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { client } from 'src/shared/api/client';
|
|
2
2
|
import { Queries } from '@coopenomics/sdk';
|
|
3
|
-
import type {
|
|
3
|
+
import type { IChatCoopAccountStatus } from '../model/types';
|
|
4
4
|
|
|
5
|
-
async function getAccountStatus(): Promise<
|
|
6
|
-
const { [Queries.
|
|
7
|
-
Queries.
|
|
5
|
+
async function getAccountStatus(): Promise<IChatCoopAccountStatus> {
|
|
6
|
+
const { [Queries.ChatCoop.GetAccountStatus.name]: output } = await client.Query(
|
|
7
|
+
Queries.ChatCoop.GetAccountStatus.query,
|
|
8
8
|
);
|
|
9
9
|
|
|
10
10
|
return {
|
package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/store.ts
RENAMED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { defineStore } from 'pinia';
|
|
2
2
|
import { ref, Ref } from 'vue';
|
|
3
3
|
import { api } from '../api';
|
|
4
|
-
import type {
|
|
4
|
+
import type { IChatCoopAccountStatus } from './types';
|
|
5
5
|
|
|
6
|
-
const namespace = '
|
|
6
|
+
const namespace = 'chatcoopChatStore';
|
|
7
7
|
|
|
8
|
-
interface
|
|
9
|
-
accountStatus: Ref<
|
|
8
|
+
interface IChatCoopChatStore {
|
|
9
|
+
accountStatus: Ref<IChatCoopAccountStatus | null>;
|
|
10
10
|
isLoading: Ref<boolean>;
|
|
11
11
|
error: Ref<string | null>;
|
|
12
|
-
loadAccountStatus: () => Promise<
|
|
12
|
+
loadAccountStatus: () => Promise<IChatCoopAccountStatus | null>;
|
|
13
13
|
clearAccountStatus: () => void;
|
|
14
14
|
clearError: () => void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export const
|
|
17
|
+
export const useChatCoopChatStore = defineStore(
|
|
18
18
|
namespace,
|
|
19
|
-
():
|
|
20
|
-
const accountStatus = ref<
|
|
19
|
+
(): IChatCoopChatStore => {
|
|
20
|
+
const accountStatus = ref<IChatCoopAccountStatus | null>(null);
|
|
21
21
|
const isLoading = ref(false);
|
|
22
22
|
const error = ref<string | null>(null);
|
|
23
23
|
|
|
24
|
-
const loadAccountStatus = async (): Promise<
|
|
24
|
+
const loadAccountStatus = async (): Promise<IChatCoopAccountStatus | null> => {
|
|
25
25
|
isLoading.value = true;
|
|
26
26
|
error.value = null;
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ export const useCoopgramChatStore = defineStore(
|
|
|
30
30
|
accountStatus.value = status;
|
|
31
31
|
return status;
|
|
32
32
|
} catch (err) {
|
|
33
|
-
console.error('Failed to load
|
|
33
|
+
console.error('Failed to load ChatCoop account status:', err);
|
|
34
34
|
error.value = 'Не удалось получить статус аккаунта. Попробуйте обновить страницу.';
|
|
35
35
|
return null;
|
|
36
36
|
} finally {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ChatCoopChat';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { markRaw } from 'vue';
|
|
2
|
-
import {
|
|
2
|
+
import { ChatCoopPage, MobileClientPage } from './pages';
|
|
3
3
|
import { agreementsBase } from 'src/shared/lib/consts/workspaces';
|
|
4
4
|
import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace';
|
|
5
5
|
|
|
6
6
|
export default async function (): Promise<IWorkspaceConfig[]> {
|
|
7
7
|
return [{
|
|
8
|
-
workspace: '
|
|
9
|
-
extension_name: '
|
|
10
|
-
title: '
|
|
8
|
+
workspace: 'chatcoop',
|
|
9
|
+
extension_name: 'chatcoop',
|
|
10
|
+
title: 'Кооперативный мессенджер',
|
|
11
11
|
icon: 'fa-solid fa-comments',
|
|
12
12
|
defaultRoute: 'chat', // Маршрут по умолчанию для рабочего стола чата
|
|
13
13
|
routes: [
|
|
@@ -17,16 +17,15 @@ export default async function (): Promise<IWorkspaceConfig[]> {
|
|
|
17
17
|
icon: 'fa-solid fa-comments',
|
|
18
18
|
roles: ['user', 'chairman', 'member'],
|
|
19
19
|
},
|
|
20
|
-
path: '/:coopname/
|
|
21
|
-
name: '
|
|
22
|
-
component: markRaw(CoopgramPage),
|
|
20
|
+
path: '/:coopname/chatcoop',
|
|
21
|
+
name: 'chatcoop',
|
|
23
22
|
children: [
|
|
24
23
|
{
|
|
25
24
|
path: 'chat',
|
|
26
|
-
name: '
|
|
27
|
-
component: markRaw(
|
|
25
|
+
name: 'chatcoop-chat',
|
|
26
|
+
component: markRaw(ChatCoopPage),
|
|
28
27
|
meta: {
|
|
29
|
-
title: '
|
|
28
|
+
title: 'Быстрый клиент',
|
|
30
29
|
icon: 'fa-solid fa-comments',
|
|
31
30
|
roles: ['user', 'chairman', 'member'],
|
|
32
31
|
agreements: agreementsBase,
|
|
@@ -34,6 +33,19 @@ export default async function (): Promise<IWorkspaceConfig[]> {
|
|
|
34
33
|
},
|
|
35
34
|
children: [],
|
|
36
35
|
},
|
|
36
|
+
{
|
|
37
|
+
path: 'mobile',
|
|
38
|
+
name: 'chatcoop-mobile',
|
|
39
|
+
component: markRaw(MobileClientPage),
|
|
40
|
+
meta: {
|
|
41
|
+
title: 'Мобильный клиент',
|
|
42
|
+
icon: 'fa-solid fa-mobile-alt',
|
|
43
|
+
roles: ['user', 'chairman', 'member'],
|
|
44
|
+
agreements: agreementsBase,
|
|
45
|
+
requiresAuth: true,
|
|
46
|
+
},
|
|
47
|
+
children: [],
|
|
48
|
+
},
|
|
37
49
|
],
|
|
38
50
|
},
|
|
39
51
|
],
|
|
@@ -11,8 +11,8 @@ export function useCreateMatrixAccount() {
|
|
|
11
11
|
error.value = null;
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
const { [Mutations.
|
|
15
|
-
Mutations.
|
|
14
|
+
const { [Mutations.ChatCoop.CreateAccount.name]: result } = await client.Mutation(
|
|
15
|
+
Mutations.ChatCoop.CreateAccount.mutation,
|
|
16
16
|
{
|
|
17
17
|
variables: {
|
|
18
18
|
data: { username, password },
|
|
@@ -1,51 +1,52 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
div
|
|
3
3
|
// Лоадер пока получаем статус аккаунта
|
|
4
|
-
WindowLoader(v-if="
|
|
4
|
+
WindowLoader(v-if="chatcoopStore.isLoading", text="Проверка статуса аккаунта...")
|
|
5
5
|
|
|
6
6
|
// Сообщение об ошибке
|
|
7
|
-
div(v-else-if="
|
|
8
|
-
p {{
|
|
7
|
+
div(v-else-if="chatcoopStore.error", class="error-message")
|
|
8
|
+
p {{ chatcoopStore.error }}
|
|
9
9
|
button(@click="retryLoadStatus", class="retry-button") Повторить попытку
|
|
10
10
|
|
|
11
11
|
// Виджет регистрации Matrix аккаунта
|
|
12
12
|
MatrixRegistration(
|
|
13
|
-
v-else-if="
|
|
13
|
+
v-else-if="chatcoopStore.accountStatus && !chatcoopStore.accountStatus.hasAccount",
|
|
14
14
|
@account-created="handleAccountCreated"
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
// Лоадер пока загружается iframe Matrix клиента
|
|
18
18
|
WindowLoader(
|
|
19
|
-
v-else-if="
|
|
20
|
-
text="Загрузка
|
|
19
|
+
v-else-if="chatcoopStore.accountStatus?.iframeUrl && isIframeLoading",
|
|
20
|
+
text="Загрузка клиента..."
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
// Iframe с Matrix клиентом после полной загрузки
|
|
24
24
|
iframe(
|
|
25
|
-
v-else-if="
|
|
25
|
+
v-else-if="chatcoopStore.accountStatus?.iframeUrl",
|
|
26
26
|
v-show="!isIframeLoading",
|
|
27
|
-
:src="
|
|
27
|
+
:src="chatcoopStore.accountStatus.iframeUrl",
|
|
28
28
|
class="matrix-iframe",
|
|
29
29
|
frameborder="0",
|
|
30
30
|
width="100%",
|
|
31
31
|
:style="{ height: 'calc(100vh - 56px)' }"
|
|
32
32
|
@load="onIframeLoaded"
|
|
33
33
|
)
|
|
34
|
+
|
|
34
35
|
</template>
|
|
35
36
|
|
|
36
37
|
<script lang="ts" setup>
|
|
37
38
|
import { ref, onMounted, watch } from 'vue';
|
|
38
39
|
import { WindowLoader } from 'src/shared/ui/Loader';
|
|
39
|
-
import {
|
|
40
|
+
import { useChatCoopChatStore } from '../../../entities/ChatCoopChat/model';
|
|
40
41
|
import { MatrixRegistration } from '../../../widgets/MatrixRegistration';
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
+
const chatcoopStore = useChatCoopChatStore();
|
|
43
44
|
const isIframeLoading = ref(true);
|
|
44
45
|
let iframeLoadTimeout: number | null = null;
|
|
45
46
|
|
|
46
47
|
// Сбрасываем состояние загрузки iframe при изменении URL
|
|
47
|
-
watch(() =>
|
|
48
|
-
if (
|
|
48
|
+
watch(() => chatcoopStore.accountStatus?.iframeUrl, () => {
|
|
49
|
+
if (chatcoopStore.accountStatus?.iframeUrl) {
|
|
49
50
|
isIframeLoading.value = true;
|
|
50
51
|
|
|
51
52
|
// Очищаем предыдущий таймаут
|
|
@@ -70,17 +71,17 @@ function onIframeLoaded() {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
async function retryLoadStatus() {
|
|
73
|
-
|
|
74
|
-
await
|
|
74
|
+
chatcoopStore.clearError();
|
|
75
|
+
await chatcoopStore.loadAccountStatus();
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
async function handleAccountCreated() {
|
|
78
79
|
// После успешного создания аккаунта перезагружаем статус
|
|
79
|
-
await
|
|
80
|
+
await chatcoopStore.loadAccountStatus();
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
onMounted(async () => {
|
|
83
|
-
await
|
|
84
|
+
await chatcoopStore.loadAccountStatus();
|
|
84
85
|
});
|
|
85
86
|
</script>
|
|
86
87
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ChatCoopPage } from './ChatCoopPage.vue';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as MobileClientPage } from './ui/MobileClientPage.vue';
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
<template lang="pug">
|
|
2
|
+
div.mobile-client-page
|
|
3
|
+
// Лоадер пока получаем статус аккаунта
|
|
4
|
+
WindowLoader(v-if="chatcoopStore.isLoading", text="Проверка статуса аккаунта...")
|
|
5
|
+
|
|
6
|
+
// Сообщение об ошибке
|
|
7
|
+
div(v-else-if="chatcoopStore.error", class="error-message")
|
|
8
|
+
p {{ chatcoopStore.error }}
|
|
9
|
+
button(@click="retryLoadStatus", class="retry-button") Повторить попытку
|
|
10
|
+
|
|
11
|
+
// Сообщение для пользователей без аккаунта
|
|
12
|
+
div(v-else-if="!chatcoopStore.accountStatus || !chatcoopStore.accountStatus.hasAccount", class="no-account-message")
|
|
13
|
+
div.no-account-icon
|
|
14
|
+
i.fas.fa-mobile-alt
|
|
15
|
+
h2 Нет аккаунта кооперативного мессенджера
|
|
16
|
+
p У кооперативного мессенджера есть мобильное приложение Element X.
|
|
17
|
+
p Для подключения вам нужен аккаунт кооперативного мессенджера.
|
|
18
|
+
p Зарегистрируйте аккаунт на главной странице и получите инструкцию для мобильного приложения.
|
|
19
|
+
router-link(to="/chatcoop/chat", class="register-button")
|
|
20
|
+
i.fas.fa-arrow-left
|
|
21
|
+
| Перейти к регистрации
|
|
22
|
+
|
|
23
|
+
// Инструкции для пользователей с аккаунтом
|
|
24
|
+
div(v-else-if="chatcoopStore.accountStatus?.hasAccount && chatcoopStore.accountStatus?.matrixUsername", class="mobile-instructions")
|
|
25
|
+
div.header
|
|
26
|
+
h1 Мобильный клиент
|
|
27
|
+
p Подключитесь к кооперативному мессенджеру через мобильное приложение Element X
|
|
28
|
+
|
|
29
|
+
div.instruction-steps
|
|
30
|
+
div.step
|
|
31
|
+
div.step-number 1
|
|
32
|
+
div.step-content
|
|
33
|
+
h3 Скачайте приложение Element X
|
|
34
|
+
p Выберите версию для вашей платформы:
|
|
35
|
+
div.download-links
|
|
36
|
+
a(href="https://play.google.com/store/apps/details?id=io.element.android.x", target="_blank", class="download-link")
|
|
37
|
+
i.fab.fa-google-play
|
|
38
|
+
| Google Play
|
|
39
|
+
a(href="https://apps.apple.com/us/app/element-x-secure-chat-call/id1631335820", target="_blank", class="download-link")
|
|
40
|
+
i.fab.fa-app-store
|
|
41
|
+
| App Store
|
|
42
|
+
a(href="https://element.io/download", target="_blank", class="download-link")
|
|
43
|
+
i.fas.fa-globe
|
|
44
|
+
| Другие платформы
|
|
45
|
+
|
|
46
|
+
div.step
|
|
47
|
+
div.step-number 2
|
|
48
|
+
div.step-content
|
|
49
|
+
h3 Войдите вручную
|
|
50
|
+
p Запустите приложение и нажмите кнопку "Войти вручную" (или "Sign in manually")
|
|
51
|
+
|
|
52
|
+
div.step
|
|
53
|
+
div.step-number 3
|
|
54
|
+
div.step-content
|
|
55
|
+
h3 Смените поставщика учетной записи
|
|
56
|
+
p Нажмите на ссылку "Сменить поставщика учетной записи" (или "Change homeserver")
|
|
57
|
+
|
|
58
|
+
div.step
|
|
59
|
+
div.step-number 4
|
|
60
|
+
div.step-content
|
|
61
|
+
h3 Введите адрес сервера
|
|
62
|
+
div.server-input
|
|
63
|
+
input(
|
|
64
|
+
id="homeserver",
|
|
65
|
+
type="text",
|
|
66
|
+
:value="homeserverUrl",
|
|
67
|
+
readonly,
|
|
68
|
+
class="server-url-input"
|
|
69
|
+
)
|
|
70
|
+
button(@click="copyToClipboard", class="copy-button")
|
|
71
|
+
i.fas.fa-copy
|
|
72
|
+
| Копировать
|
|
73
|
+
|
|
74
|
+
div.step
|
|
75
|
+
div.step-number 5
|
|
76
|
+
div.step-content
|
|
77
|
+
h3 Войдите под своими учетными данными
|
|
78
|
+
p Используйте данные вашего аккаунта кооперативного мессенджера:
|
|
79
|
+
ul.credentials-list
|
|
80
|
+
li
|
|
81
|
+
strong Имя пользователя:
|
|
82
|
+
| {{ chatcoopStore.accountStatus?.matrixUsername || 'Загружается...' }}
|
|
83
|
+
li
|
|
84
|
+
strong Пароль:
|
|
85
|
+
| Получен при регистрации аккаунта
|
|
86
|
+
|
|
87
|
+
// Сообщение если аккаунт есть, но нет matrixUsername
|
|
88
|
+
div(v-else-if="chatcoopStore.accountStatus?.hasAccount && !chatcoopStore.accountStatus?.matrixUsername", class="error-message")
|
|
89
|
+
p У вас есть аккаунт кооперативного мессенджера, но отсутствуют данные для входа в мобильное приложение.
|
|
90
|
+
p Попробуйте обновить страницу или обратитесь в поддержку.
|
|
91
|
+
button(@click="retryLoadStatus", class="retry-button") Обновить данные
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<script lang="ts" setup>
|
|
95
|
+
import { computed, onMounted } from 'vue';
|
|
96
|
+
import { WindowLoader } from 'src/shared/ui/Loader';
|
|
97
|
+
import { SuccessAlert } from 'src/shared/api';
|
|
98
|
+
import { useChatCoopChatStore } from '../../../entities/ChatCoopChat/model';
|
|
99
|
+
|
|
100
|
+
const chatcoopStore = useChatCoopChatStore();
|
|
101
|
+
|
|
102
|
+
const homeserverUrl = computed(() => {
|
|
103
|
+
const username = chatcoopStore.accountStatus?.matrixUsername;
|
|
104
|
+
if (username && username.includes(':')) {
|
|
105
|
+
return username.split(':')[1];
|
|
106
|
+
}
|
|
107
|
+
return 'chat.coopenomics.world';
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
async function retryLoadStatus() {
|
|
111
|
+
chatcoopStore.clearError();
|
|
112
|
+
await chatcoopStore.loadAccountStatus();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function copyToClipboard() {
|
|
116
|
+
try {
|
|
117
|
+
await navigator.clipboard.writeText(homeserverUrl.value);
|
|
118
|
+
SuccessAlert('Адрес сервера скопирован в буфер обмена');
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.error('Failed to copy to clipboard:', err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onMounted(async () => {
|
|
125
|
+
await chatcoopStore.loadAccountStatus();
|
|
126
|
+
});
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<style scoped>
|
|
130
|
+
.mobile-client-page {
|
|
131
|
+
padding: 2rem;
|
|
132
|
+
max-width: 800px;
|
|
133
|
+
margin: 0 auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.no-account-message {
|
|
137
|
+
text-align: center;
|
|
138
|
+
padding: 3rem 2rem;
|
|
139
|
+
background: #f8f9fa;
|
|
140
|
+
border-radius: 8px;
|
|
141
|
+
border: 2px dashed #dee2e6;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.no-account-icon {
|
|
145
|
+
font-size: 4rem;
|
|
146
|
+
color: #6c757d;
|
|
147
|
+
margin-bottom: 1.5rem;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.no-account-message h2 {
|
|
151
|
+
color: #495057;
|
|
152
|
+
margin-bottom: 1rem;
|
|
153
|
+
font-size: 1.5rem;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.no-account-message p {
|
|
157
|
+
color: #6c757d;
|
|
158
|
+
margin-bottom: 1rem;
|
|
159
|
+
line-height: 1.6;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.register-button {
|
|
163
|
+
display: inline-flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: 0.5rem;
|
|
166
|
+
padding: 0.75rem 1.5rem;
|
|
167
|
+
background: #007bff;
|
|
168
|
+
color: white;
|
|
169
|
+
text-decoration: none;
|
|
170
|
+
border-radius: 6px;
|
|
171
|
+
font-weight: 500;
|
|
172
|
+
transition: background-color 0.2s;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.register-button:hover {
|
|
176
|
+
background: #0056b3;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.mobile-instructions {
|
|
180
|
+
background: white;
|
|
181
|
+
border-radius: 8px;
|
|
182
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
183
|
+
overflow: hidden;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.header {
|
|
187
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
188
|
+
color: white;
|
|
189
|
+
padding: 2rem;
|
|
190
|
+
text-align: center;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.header h1 {
|
|
194
|
+
margin: 0 0 0.5rem 0;
|
|
195
|
+
font-size: 2rem;
|
|
196
|
+
font-weight: 600;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.header p {
|
|
200
|
+
margin: 0;
|
|
201
|
+
opacity: 0.9;
|
|
202
|
+
font-size: 1.1rem;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.instruction-steps {
|
|
206
|
+
padding: 2rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.step {
|
|
210
|
+
display: flex;
|
|
211
|
+
gap: 1.5rem;
|
|
212
|
+
margin-bottom: 2rem;
|
|
213
|
+
align-items: flex-start;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.step-number {
|
|
217
|
+
display: flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
justify-content: center;
|
|
220
|
+
width: 40px;
|
|
221
|
+
height: 40px;
|
|
222
|
+
background: #007bff;
|
|
223
|
+
color: white;
|
|
224
|
+
border-radius: 50%;
|
|
225
|
+
font-weight: bold;
|
|
226
|
+
font-size: 1.2rem;
|
|
227
|
+
flex-shrink: 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.step-content h3 {
|
|
231
|
+
margin: 0 0 0.5rem 0;
|
|
232
|
+
color: #495057;
|
|
233
|
+
font-size: 1.2rem;
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.step-content p {
|
|
238
|
+
margin: 0 0 1rem 0;
|
|
239
|
+
color: #6c757d;
|
|
240
|
+
line-height: 1.6;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.download-links {
|
|
244
|
+
display: flex;
|
|
245
|
+
gap: 1rem;
|
|
246
|
+
flex-wrap: wrap;
|
|
247
|
+
margin-top: 1rem;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.download-link {
|
|
251
|
+
display: inline-flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
gap: 0.5rem;
|
|
254
|
+
padding: 0.5rem 1rem;
|
|
255
|
+
background: #f8f9fa;
|
|
256
|
+
color: #495057;
|
|
257
|
+
text-decoration: none;
|
|
258
|
+
border-radius: 6px;
|
|
259
|
+
border: 1px solid #dee2e6;
|
|
260
|
+
transition: all 0.2s;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.download-link:hover {
|
|
264
|
+
background: #e9ecef;
|
|
265
|
+
border-color: #adb5bd;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.server-input {
|
|
269
|
+
margin-top: 1rem;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.server-input label {
|
|
273
|
+
display: block;
|
|
274
|
+
margin-bottom: 0.5rem;
|
|
275
|
+
font-weight: 500;
|
|
276
|
+
color: #495057;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.server-url-input {
|
|
280
|
+
width: 100%;
|
|
281
|
+
padding: 0.75rem;
|
|
282
|
+
border: 2px solid #dee2e6;
|
|
283
|
+
border-radius: 6px;
|
|
284
|
+
font-family: monospace;
|
|
285
|
+
font-size: 1rem;
|
|
286
|
+
background: #f8f9fa;
|
|
287
|
+
margin-bottom: 0.5rem;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.copy-button {
|
|
291
|
+
display: inline-flex;
|
|
292
|
+
align-items: center;
|
|
293
|
+
gap: 0.5rem;
|
|
294
|
+
padding: 0.5rem 1rem;
|
|
295
|
+
background: #28a745;
|
|
296
|
+
color: white;
|
|
297
|
+
border: none;
|
|
298
|
+
border-radius: 6px;
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
font-size: 0.9rem;
|
|
301
|
+
transition: background-color 0.2s;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.copy-button:hover {
|
|
305
|
+
background: #218838;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.credentials-list {
|
|
309
|
+
background: #f8f9fa;
|
|
310
|
+
padding: 1rem;
|
|
311
|
+
border-radius: 6px;
|
|
312
|
+
border-left: 4px solid #007bff;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.credentials-list li {
|
|
316
|
+
margin-bottom: 0.5rem;
|
|
317
|
+
line-height: 1.5;
|
|
318
|
+
color: #495057;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.credentials-list li:last-child {
|
|
322
|
+
margin-bottom: 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.account-info {
|
|
326
|
+
background: #f8f9fa;
|
|
327
|
+
padding: 1.5rem;
|
|
328
|
+
margin: 2rem;
|
|
329
|
+
border-radius: 8px;
|
|
330
|
+
border: 1px solid #dee2e6;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.account-info h3 {
|
|
334
|
+
margin: 0 0 1rem 0;
|
|
335
|
+
color: #495057;
|
|
336
|
+
font-size: 1.1rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.info-item {
|
|
340
|
+
display: flex;
|
|
341
|
+
justify-content: space-between;
|
|
342
|
+
align-items: center;
|
|
343
|
+
padding: 0.5rem 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.info-item .label {
|
|
347
|
+
font-weight: 500;
|
|
348
|
+
color: #495057;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.info-item .value {
|
|
352
|
+
font-family: monospace;
|
|
353
|
+
background: white;
|
|
354
|
+
padding: 0.25rem 0.5rem;
|
|
355
|
+
border-radius: 4px;
|
|
356
|
+
border: 1px solid #dee2e6;
|
|
357
|
+
color: #495057;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.error-message {
|
|
361
|
+
display: flex;
|
|
362
|
+
flex-direction: column;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: center;
|
|
365
|
+
height: 50vh;
|
|
366
|
+
text-align: center;
|
|
367
|
+
padding: 2rem;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.error-message p {
|
|
371
|
+
margin-bottom: 1rem;
|
|
372
|
+
color: #dc3545;
|
|
373
|
+
font-size: 1.1rem;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.retry-button {
|
|
377
|
+
padding: 0.5rem 1rem;
|
|
378
|
+
background-color: #007bff;
|
|
379
|
+
color: white;
|
|
380
|
+
border: none;
|
|
381
|
+
border-radius: 0.25rem;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
font-size: 1rem;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.retry-button:hover {
|
|
387
|
+
background-color: #0056b3;
|
|
388
|
+
}
|
|
389
|
+
</style>
|
package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/model/useMatrixRegistration.ts
RENAMED
|
@@ -46,8 +46,8 @@ export function useMatrixRegistration() {
|
|
|
46
46
|
|
|
47
47
|
checkingUsername.value = true;
|
|
48
48
|
try {
|
|
49
|
-
const { [Queries.
|
|
50
|
-
Queries.
|
|
49
|
+
const { [Queries.ChatCoop.CheckUsernameAvailability.name]: result } = await client.Query(
|
|
50
|
+
Queries.ChatCoop.CheckUsernameAvailability.query,
|
|
51
51
|
{
|
|
52
52
|
variables: {
|
|
53
53
|
data: { username: value },
|
package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/ui/MatrixRegistration.vue
RENAMED
|
@@ -87,7 +87,7 @@ div.matrix-registration
|
|
|
87
87
|
<script lang="ts" setup>
|
|
88
88
|
import { computed } from 'vue';
|
|
89
89
|
import { useMatrixRegistration } from '../model/useMatrixRegistration';
|
|
90
|
-
import { useCreateMatrixAccount } from '../../../pages/
|
|
90
|
+
import { useCreateMatrixAccount } from '../../../pages/ChatCoopPage/features/CreateMatrixAccount/model/useCreateMatrixAccount';
|
|
91
91
|
import { useSessionStore } from 'src/entities/Session/model/store';
|
|
92
92
|
|
|
93
93
|
const sessionStore = useSessionStore();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coopenomics/desktop",
|
|
3
|
-
"version": "2025.11.24-alpha-
|
|
3
|
+
"version": "2025.11.24-alpha-3",
|
|
4
4
|
"description": "A Desktop Project",
|
|
5
5
|
"productName": "Desktop App",
|
|
6
6
|
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"start": "node -r ./alias-resolver.js dist/ssr/index.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@coopenomics/controller": "2025.11.24-alpha-
|
|
29
|
-
"@coopenomics/notifications": "2025.11.24-alpha-
|
|
30
|
-
"@coopenomics/sdk": "2025.11.24-alpha-
|
|
28
|
+
"@coopenomics/controller": "2025.11.24-alpha-3",
|
|
29
|
+
"@coopenomics/notifications": "2025.11.24-alpha-3",
|
|
30
|
+
"@coopenomics/sdk": "2025.11.24-alpha-3",
|
|
31
31
|
"@dicebear/collection": "^9.0.1",
|
|
32
32
|
"@dicebear/core": "^9.0.1",
|
|
33
33
|
"@editorjs/code": "^2.9.3",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
|
|
60
60
|
"axios": "^1.2.1",
|
|
61
61
|
"compression": "^1.7.4",
|
|
62
|
-
"cooptypes": "2025.11.24-alpha-
|
|
62
|
+
"cooptypes": "2025.11.24-alpha-3",
|
|
63
63
|
"dompurify": "^3.1.7",
|
|
64
64
|
"dotenv": "^16.4.5",
|
|
65
65
|
"email-regex": "^5.0.0",
|
|
@@ -123,5 +123,5 @@
|
|
|
123
123
|
"npm": ">= 6.13.4",
|
|
124
124
|
"yarn": ">= 1.21.1"
|
|
125
125
|
},
|
|
126
|
-
"gitHead": "
|
|
126
|
+
"gitHead": "e16d2ba2385aec48013d23631958e822a549e109"
|
|
127
127
|
}
|
|
@@ -32,6 +32,7 @@ import { useExtensionStore } from 'src/entities/Extension/model';
|
|
|
32
32
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
33
33
|
import { useRoute, useRouter } from 'vue-router';
|
|
34
34
|
import { useBackButton } from 'src/shared/lib/navigation';
|
|
35
|
+
import { isExtensionSchemaEmpty } from 'src/shared/lib/utils';
|
|
35
36
|
import { ExtensionImage } from 'src/widgets/ExtensionImage';
|
|
36
37
|
import { ExtensionActions } from 'src/widgets/ExtensionActions';
|
|
37
38
|
import { ExtensionInfo } from 'src/widgets/ExtensionInfo';
|
|
@@ -87,11 +88,7 @@ const currentMode = computed(() => {
|
|
|
87
88
|
return 'main';
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
const isEmpty = computed(() =>
|
|
91
|
-
if (extension.value?.schema)
|
|
92
|
-
return Object.keys(extension.value?.schema.properties).length == 0;
|
|
93
|
-
else return false;
|
|
94
|
-
});
|
|
91
|
+
const isEmpty = computed(() => isExtensionSchemaEmpty(extension.value?.schema));
|
|
95
92
|
</script>
|
|
96
93
|
|
|
97
94
|
<style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Проверяет, является ли схема настроек расширения пустой.
|
|
3
|
+
* Схема считается пустой, если:
|
|
4
|
+
* - У схемы нет свойств
|
|
5
|
+
* - Все свойства имеют visible: false в description
|
|
6
|
+
*/
|
|
7
|
+
export function isExtensionSchemaEmpty(schema: any): boolean {
|
|
8
|
+
if (!schema?.properties) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const properties = Object.values(schema.properties) as any[];
|
|
13
|
+
|
|
14
|
+
// Проверяем, есть ли хотя бы одно видимое свойство
|
|
15
|
+
const hasVisibleProperties = properties.some((property: any) => {
|
|
16
|
+
return property?.description?.visible !== false;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return !hasVisibleProperties;
|
|
20
|
+
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { computed, defineAsyncComponent } from 'vue';
|
|
22
22
|
import { ZodForm } from 'src/shared/ui/ZodForm';
|
|
23
23
|
import { ClientOnly } from 'src/shared/ui/ClientOnly';
|
|
24
|
+
import { isExtensionSchemaEmpty } from 'src/shared/lib/utils';
|
|
24
25
|
|
|
25
26
|
// Клиентский компонент для markdown, загружаемый только на клиенте
|
|
26
27
|
const VueMarkdown = defineAsyncComponent(() =>
|
|
@@ -36,9 +37,5 @@ interface Props {
|
|
|
36
37
|
|
|
37
38
|
const props = defineProps<Props>();
|
|
38
39
|
|
|
39
|
-
const isEmpty = computed(() =>
|
|
40
|
-
if (props.schema)
|
|
41
|
-
return Object.keys(props.schema.properties).length == 0;
|
|
42
|
-
else return false;
|
|
43
|
-
});
|
|
40
|
+
const isEmpty = computed(() => isExtensionSchemaEmpty(props.schema));
|
|
44
41
|
</script>
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
<script lang="ts" setup>
|
|
18
18
|
import { computed } from 'vue';
|
|
19
19
|
import { ZodForm } from 'src/shared/ui/ZodForm';
|
|
20
|
+
import { isExtensionSchemaEmpty } from 'src/shared/lib/utils';
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
schema?: any;
|
|
@@ -26,11 +27,7 @@ interface Props {
|
|
|
26
27
|
|
|
27
28
|
const props = defineProps<Props>();
|
|
28
29
|
|
|
29
|
-
const isEmpty = computed(() =>
|
|
30
|
-
if (props.schema)
|
|
31
|
-
return Object.keys(props.schema.properties).length == 0;
|
|
32
|
-
else return false;
|
|
33
|
-
});
|
|
30
|
+
const isEmpty = computed(() => isExtensionSchemaEmpty(props.schema));
|
|
34
31
|
</script>
|
|
35
32
|
|
|
36
33
|
<style lang="scss" scoped>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './CoopgramChat';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as CoopgramPage } from './CoopgramPage.vue';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { CoopgramPage } from './CoopgramPage';
|
/package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/index.ts
RENAMED
|
File without changes
|
/package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/index.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|