@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.
Files changed (32) hide show
  1. package/extensions/{coopgram → chatcoop}/README.md +12 -12
  2. package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/api/index.ts +4 -4
  3. package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/store.ts +10 -10
  4. package/extensions/chatcoop/entities/ChatCoopChat/model/types.ts +3 -0
  5. package/extensions/chatcoop/entities/index.ts +1 -0
  6. package/extensions/{coopgram → chatcoop}/install.ts +22 -10
  7. package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/model/useCreateMatrixAccount.ts +2 -2
  8. package/extensions/{coopgram/pages/CoopgramPage/ui/CoopgramPage.vue → chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue} +17 -16
  9. package/extensions/chatcoop/pages/ChatCoopPage/ui/index.ts +1 -0
  10. package/extensions/chatcoop/pages/MobileClientPage/index.ts +1 -0
  11. package/extensions/chatcoop/pages/MobileClientPage/ui/MobileClientPage.vue +389 -0
  12. package/extensions/chatcoop/pages/index.ts +2 -0
  13. package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/model/useMatrixRegistration.ts +2 -2
  14. package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/ui/MatrixRegistration.vue +1 -1
  15. package/package.json +6 -6
  16. package/src/pages/ExtensionStore/ExtensionPage/ExtensionPage.vue +2 -5
  17. package/src/shared/lib/utils/index.ts +1 -0
  18. package/src/shared/lib/utils/schema.ts +20 -0
  19. package/src/widgets/ExtensionInstall/ui/ExtensionInstall.vue +2 -5
  20. package/src/widgets/ExtensionSettings/ui/ExtensionSettings.vue +2 -5
  21. package/extensions/coopgram/entities/CoopgramChat/model/types.ts +0 -3
  22. package/extensions/coopgram/entities/index.ts +0 -1
  23. package/extensions/coopgram/pages/CoopgramPage/ui/index.ts +0 -1
  24. package/extensions/coopgram/pages/index.ts +0 -1
  25. /package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/index.ts +0 -0
  26. /package/extensions/{coopgram/entities/CoopgramChat → chatcoop/entities/ChatCoopChat}/model/index.ts +0 -0
  27. /package/extensions/{coopgram → chatcoop}/index.ts +0 -0
  28. /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/index.ts +0 -0
  29. /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/features/CreateMatrixAccount/ui/CreateMatrixAccountButton.vue +0 -0
  30. /package/extensions/{coopgram/pages/CoopgramPage → chatcoop/pages/ChatCoopPage}/index.ts +0 -0
  31. /package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/index.ts +0 -0
  32. /package/extensions/{coopgram → chatcoop}/widgets/MatrixRegistration/model/types.ts +0 -0
@@ -1,10 +1,10 @@
1
- # Coopgram Extension
1
+ # ChatCoop Extension
2
2
 
3
3
  Расширение для интеграции Matrix чата в кооперативную систему.
4
4
 
5
5
  ## Описание
6
6
 
7
- Coopgram предоставляет встроенный Matrix чат для пользователей кооператива. Расширение:
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
- coopgram/
16
+ chatcoop/
17
17
  ├── install.ts # Конфигурация рабочего стола
18
18
  ├── entities/
19
- │ └── CoopgramChat/ # Entity для работы с Matrix токенами
19
+ │ └── ChatCoopChat/ # Entity для работы с Matrix токенами
20
20
  │ ├── api/ # GraphQL запросы через SDK
21
21
  │ └── model/ # Store и типы данных из SDK
22
22
  ├── pages/
23
- │ └── CoopgramPage/ # Главная страница с iframe
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.Coopgram.GetToken`
33
- - **Selector**: `coopgramTokenSelector`
32
+ - **Query**: `Queries.ChatCoop.GetToken`
33
+ - **Selector**: `chatcoopTokenSelector`
34
34
  - **Типы**: Автоматически генерируются из GraphQL схемы
35
35
 
36
36
  ## Работа с iframe URL
37
37
 
38
- 1. При загрузке страницы вызывается `coopgramStore.loadToken()`
39
- 2. Entity отправляет типизированный GraphQL запрос через SDK: `Queries.Coopgram.GetToken`
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**: 'coopgram'
49
+ - **workspace**: 'chatcoop'
50
50
  - **defaultRoute**: 'chat'
51
51
 
52
52
  ## Архитектура
53
53
 
54
54
  Расширение построено по принципам Feature-Sliced Design (FSD):
55
55
 
56
- - **Entities**: `CoopgramChat` - бизнес-сущность для работы с Matrix токенами
57
- - **Pages**: `CoopgramPage` - UI страница без бизнес-логики
56
+ - **Entities**: `ChatCoopChat` - бизнес-сущность для работы с Matrix токенами
57
+ - **Pages**: `ChatCoopPage` - UI страница без бизнес-логики
58
58
  - **Store**: Pinia store для управления состоянием токена
59
59
  - **API**: Функции для GraphQL запросов
60
60
 
@@ -1,10 +1,10 @@
1
1
  import { client } from 'src/shared/api/client';
2
2
  import { Queries } from '@coopenomics/sdk';
3
- import type { ICoopgramAccountStatus } from '../model/types';
3
+ import type { IChatCoopAccountStatus } from '../model/types';
4
4
 
5
- async function getAccountStatus(): Promise<ICoopgramAccountStatus> {
6
- const { [Queries.Coopgram.GetAccountStatus.name]: output } = await client.Query(
7
- Queries.Coopgram.GetAccountStatus.query,
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 {
@@ -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 { ICoopgramAccountStatus } from './types';
4
+ import type { IChatCoopAccountStatus } from './types';
5
5
 
6
- const namespace = 'coopgramChatStore';
6
+ const namespace = 'chatcoopChatStore';
7
7
 
8
- interface ICoopgramChatStore {
9
- accountStatus: Ref<ICoopgramAccountStatus | null>;
8
+ interface IChatCoopChatStore {
9
+ accountStatus: Ref<IChatCoopAccountStatus | null>;
10
10
  isLoading: Ref<boolean>;
11
11
  error: Ref<string | null>;
12
- loadAccountStatus: () => Promise<ICoopgramAccountStatus | null>;
12
+ loadAccountStatus: () => Promise<IChatCoopAccountStatus | null>;
13
13
  clearAccountStatus: () => void;
14
14
  clearError: () => void;
15
15
  }
16
16
 
17
- export const useCoopgramChatStore = defineStore(
17
+ export const useChatCoopChatStore = defineStore(
18
18
  namespace,
19
- (): ICoopgramChatStore => {
20
- const accountStatus = ref<ICoopgramAccountStatus | null>(null);
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<ICoopgramAccountStatus | null> => {
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 Coopgram account status:', err);
33
+ console.error('Failed to load ChatCoop account status:', err);
34
34
  error.value = 'Не удалось получить статус аккаунта. Попробуйте обновить страницу.';
35
35
  return null;
36
36
  } finally {
@@ -0,0 +1,3 @@
1
+ import type { Queries } from '@coopenomics/sdk';
2
+
3
+ export type IChatCoopAccountStatus = Queries.ChatCoop.GetAccountStatus.IOutput[typeof Queries.ChatCoop.GetAccountStatus.name];
@@ -0,0 +1 @@
1
+ export * from './ChatCoopChat';
@@ -1,13 +1,13 @@
1
1
  import { markRaw } from 'vue';
2
- import { CoopgramPage } from './pages/CoopgramPage';
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: 'coopgram',
9
- extension_name: 'coopgram',
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/coopgram',
21
- name: 'coopgram',
22
- component: markRaw(CoopgramPage),
20
+ path: '/:coopname/chatcoop',
21
+ name: 'chatcoop',
23
22
  children: [
24
23
  {
25
24
  path: 'chat',
26
- name: 'coopgram-chat',
27
- component: markRaw(CoopgramPage),
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.Coopgram.CreateAccount.name]: result } = await client.Mutation(
15
- Mutations.Coopgram.CreateAccount.mutation,
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="coopgramStore.isLoading", text="Проверка статуса аккаунта...")
4
+ WindowLoader(v-if="chatcoopStore.isLoading", text="Проверка статуса аккаунта...")
5
5
 
6
6
  // Сообщение об ошибке
7
- div(v-else-if="coopgramStore.error", class="error-message")
8
- p {{ coopgramStore.error }}
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="coopgramStore.accountStatus && !coopgramStore.accountStatus.hasAccount",
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="coopgramStore.accountStatus?.iframeUrl && isIframeLoading",
20
- text="Загрузка Matrix клиента..."
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="coopgramStore.accountStatus?.iframeUrl",
25
+ v-else-if="chatcoopStore.accountStatus?.iframeUrl",
26
26
  v-show="!isIframeLoading",
27
- :src="coopgramStore.accountStatus.iframeUrl",
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 { useCoopgramChatStore } from '../../../entities/CoopgramChat/model';
40
+ import { useChatCoopChatStore } from '../../../entities/ChatCoopChat/model';
40
41
  import { MatrixRegistration } from '../../../widgets/MatrixRegistration';
41
42
 
42
- const coopgramStore = useCoopgramChatStore();
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(() => coopgramStore.accountStatus?.iframeUrl, () => {
48
- if (coopgramStore.accountStatus?.iframeUrl) {
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
- coopgramStore.clearError();
74
- await coopgramStore.loadAccountStatus();
74
+ chatcoopStore.clearError();
75
+ await chatcoopStore.loadAccountStatus();
75
76
  }
76
77
 
77
78
  async function handleAccountCreated() {
78
79
  // После успешного создания аккаунта перезагружаем статус
79
- await coopgramStore.loadAccountStatus();
80
+ await chatcoopStore.loadAccountStatus();
80
81
  }
81
82
 
82
83
  onMounted(async () => {
83
- await coopgramStore.loadAccountStatus();
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>
@@ -0,0 +1,2 @@
1
+ export { ChatCoopPage } from './ChatCoopPage';
2
+ export { MobileClientPage } from './MobileClientPage';
@@ -46,8 +46,8 @@ export function useMatrixRegistration() {
46
46
 
47
47
  checkingUsername.value = true;
48
48
  try {
49
- const { [Queries.Coopgram.CheckUsernameAvailability.name]: result } = await client.Query(
50
- Queries.Coopgram.CheckUsernameAvailability.query,
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 },
@@ -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/CoopgramPage/features/CreateMatrixAccount/model/useCreateMatrixAccount';
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-2",
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-2",
29
- "@coopenomics/notifications": "2025.11.24-alpha-2",
30
- "@coopenomics/sdk": "2025.11.24-alpha-2",
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-2",
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": "c47b99f9ee0bc70966c2ae8b3605a0c63a7a1360"
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>
@@ -13,3 +13,4 @@ export * from './editorjs';
13
13
  export * from './generateInitials';
14
14
  export * from './formatContributorName';
15
15
  export * from './pluralizeHours';
16
+ export * from './schema';
@@ -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,3 +0,0 @@
1
- import type { Queries } from '@coopenomics/sdk';
2
-
3
- export type ICoopgramAccountStatus = Queries.Coopgram.GetAccountStatus.IOutput[typeof Queries.Coopgram.GetAccountStatus.name];
@@ -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';
File without changes