Updated New Room Page
This commit is contained in:
parent
5294069b20
commit
a700e19990
30 changed files with 1263 additions and 763 deletions
|
|
@ -53,6 +53,9 @@ The app loads runtime configutation from the server at "./config.json" and merge
|
|||
* **accentColor** - The accent color of the app UI. Use a HTML-style color value string, like "#ff0080".
|
||||
* **show_status_messages** - Whether to show only user joins/leaves and display name updates, or the full range of room status updates. Possible values are "never" (only the above), "moderators" (moderators will see all status updates) or "always" (everyone will see all status updates). Defaults to "always".
|
||||
* **maxSizeAutoDownloads** - Attachments smaller than this will be auto downloaded. Default is 10Mb.
|
||||
* **roomTypes** - Available room types. This affects what is shown on the /create route, as well as access to routes /createroom, /createchannel and /createfiledrop. It should be an array with possible values "group_chat", "channel" or "file_drop".
|
||||
|
||||
Defaults to all values, ["group_chat", "channel", "file_drop"].
|
||||
|
||||
### Sticker short codes - To enable sticker short codes, follow these steps:
|
||||
* Run the "create sticker config" script using "npm run create-sticker-config <path-to-sticker-packs>"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ $app-background: #f6f6f6;
|
|||
$main-desktop-width: 900px;
|
||||
$dialog-desktop-width: 940px;
|
||||
$very-very-purple: #536dfe;
|
||||
$light-purple: #8E9FF8;
|
||||
$lighter-gray: #FDFBF9;
|
||||
|
||||
$chat-background: $background;
|
||||
|
|
|
|||
|
|
@ -1066,7 +1066,7 @@ body {
|
|||
|
||||
.option-text {
|
||||
font-size: 13 * $chat-text-size;
|
||||
border-top: 1px solid #e1e1e1;
|
||||
// border-top: 1px solid #e1e1e1;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
|
|
@ -1075,6 +1075,17 @@ body {
|
|||
font-size: 16 * $chat-text-size;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
font-weight: bold;
|
||||
font-size: 12 * $chat-text-size;
|
||||
[dir="rtl"] & {
|
||||
.v-icon {
|
||||
// Mirror the icon
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-button-left {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
|
@ -1281,57 +1292,6 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.create-room {
|
||||
background-color: $background;
|
||||
|
||||
.v-avatar {
|
||||
border: 1px solid #808080 !important;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: white;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
border: 1px solid #808080 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
color: black;
|
||||
font-size: 14 * $chat-text-size;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
}
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.room-option {
|
||||
.v-input {
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.option-warning {
|
||||
background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED;
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
.v-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.room-link .v-input__slot::before {
|
||||
/* Remove text underline */
|
||||
border-style: none !important;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,64 @@
|
|||
@import "@/assets/css/main.scss";
|
||||
|
||||
|
||||
.create-root {
|
||||
background-color: $background;
|
||||
user-select: none;
|
||||
|
||||
.v-avatar {
|
||||
border: 1px solid #808080 !important;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: white;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
border: 1px solid #808080 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
color: black;
|
||||
font-size: 14 * $chat-text-size;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
}
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.room-option {
|
||||
.v-input {
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.option-warning {
|
||||
background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED;
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
.v-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Make secondary inputs (e.g. topic) */
|
||||
.v-input.v-text-field--filled.v-text-field--rounded .v-input__slot {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.create-loggedin {
|
||||
text-align: center;
|
||||
button {
|
||||
|
|
@ -34,15 +90,12 @@
|
|||
|
||||
.create-title {
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-family: "Poppins";
|
||||
font-size: 28px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 108.5%; /* 30.38px */
|
||||
letter-spacing: -0.8px;
|
||||
white-space: pre-line;
|
||||
margin-top: 50px;
|
||||
&.no-icon {
|
||||
margin-top: 122px;
|
||||
}
|
||||
}
|
||||
|
||||
.create-info {
|
||||
|
|
@ -135,4 +188,10 @@
|
|||
flex-direction: column;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.v-btn:not(.back-button) {
|
||||
border-radius: 24px;
|
||||
min-height: 48px !important;
|
||||
min-width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
76
src/assets/css/typography.scss
Normal file
76
src/assets/css/typography.scss
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
@import "@/assets/css/main.scss";
|
||||
|
||||
.h3-bold {
|
||||
font-family: "Poppins";
|
||||
font-style: normal;
|
||||
font-size: 24 * $chat-text-size;
|
||||
font-weight: 700;
|
||||
line-height: 28.8 * $chat-text-size;
|
||||
text-align: center;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
font-family: "Inter";
|
||||
font-size: 16 * $chat-text-size;
|
||||
font-weight: 400;
|
||||
line-height: 18.72 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: left;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
color: rgba(0,0,0, 0.7);
|
||||
&.paragraph-button {
|
||||
color: $very-very-purple;
|
||||
}
|
||||
}
|
||||
|
||||
.paragraph-bold {
|
||||
font-family: "Inter";
|
||||
font-size: 16 * $chat-text-size;
|
||||
font-weight: 600;
|
||||
line-height: 18.72 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: left;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
color: rgba(0,0,0);
|
||||
margin-bottom: 3 * $chat-text-size;
|
||||
}
|
||||
|
||||
.caption-1 {
|
||||
font-family: "Inter";
|
||||
font-size: 14 * $chat-text-size;
|
||||
font-weight: 400;
|
||||
line-height: 16.38 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: left;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
color: rgba(0,0,0,0.65);
|
||||
}
|
||||
|
||||
.caption-1-gray {
|
||||
font-family: "Inter";
|
||||
font-size: 14 * $chat-text-size;
|
||||
font-weight: 400;
|
||||
line-height: 16.38 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: left;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
color: rgba(0,0,0,0.65);
|
||||
}
|
||||
|
||||
.caption-2 {
|
||||
font-family: "Inter";
|
||||
font-size: 10 * $chat-text-size;
|
||||
font-weight: 400;
|
||||
line-height: 11.7 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: left;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
color: rgba(0,0,0,0.60);
|
||||
}
|
||||
17
src/assets/icons/ic_camera.vue
Normal file
17
src/assets/icons/ic_camera.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<svg width="23" height="22" viewBox="0 0 23 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M0.700195 4.59961C0.700195 2.39047 2.49106 0.599609 4.7002 0.599609H18.0967C20.3059 0.599609 22.0967 2.39047 22.0967 4.59961V21.9962H4.7002C2.49106 21.9962 0.700195 20.2053 0.700195 17.9962V4.59961Z"
|
||||
fill="#FF3913" />
|
||||
<g clip-path="url(#clip0_704_4275)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M17.9502 7.48911C18.1987 7.58811 18.4272 7.73711 18.6202 7.92961C18.9917 8.30111 19.2002 8.80511 19.2002 9.33011C19.2002 11.1126 19.2002 14.0866 19.2002 15.8691C19.2002 16.3941 18.9917 16.8981 18.6202 17.2696C18.2487 17.6411 17.7447 17.8496 17.2197 17.8496C14.5297 17.8496 8.8707 17.8496 6.1807 17.8496C5.6557 17.8496 5.1517 17.6411 4.7802 17.2696C4.4087 16.8981 4.2002 16.3941 4.2002 15.8691C4.2002 13.9451 4.2002 10.6311 4.2002 8.92861C4.2002 8.50961 4.3667 8.10811 4.6627 7.81211C4.9587 7.51611 5.3602 7.34961 5.7792 7.34961C5.7797 7.34961 5.7797 7.34961 5.7802 7.34961C6.0082 7.34961 6.2037 7.18611 6.2437 6.96161C6.2557 6.89561 6.2677 6.82861 6.2797 6.76161C6.4252 5.94461 7.1357 5.34961 7.9657 5.34961C9.1847 5.34961 11.2157 5.34961 12.4347 5.34961C13.2647 5.34961 13.9752 5.94461 14.1207 6.76161L14.1212 6.76211C14.1742 7.06111 14.4097 7.28861 14.7002 7.33911V6.96061C14.7002 6.73261 14.7907 6.51311 14.9522 6.35161C15.1137 6.19011 15.3332 6.09961 15.5612 6.09961C15.9812 6.09961 16.6692 6.09961 17.0892 6.09961C17.3172 6.09961 17.5367 6.19011 17.6982 6.35161C17.8597 6.51311 17.9502 6.73261 17.9502 6.96061V7.48911ZM10.2002 8.34961C7.9927 8.34961 6.2002 10.1421 6.2002 12.3496C6.2002 14.5571 7.9927 16.3496 10.2002 16.3496C12.4077 16.3496 14.2002 14.5571 14.2002 12.3496C14.2002 10.1421 12.4077 8.34961 10.2002 8.34961ZM17.9502 9.76911C17.9502 9.52511 17.8532 9.29111 17.6807 9.11861C17.5087 8.94661 17.2747 8.84961 17.0307 8.84961C16.7552 8.84961 16.3952 8.84961 16.1197 8.84961C15.8757 8.84961 15.6417 8.94661 15.4697 9.11861C15.2972 9.29111 15.2002 9.52511 15.2002 9.76911C15.2002 9.97561 15.2002 10.2236 15.2002 10.4301C15.2002 10.6741 15.2972 10.9081 15.4697 11.0806C15.6417 11.2526 15.8757 11.3496 16.1197 11.3496C16.3952 11.3496 16.7552 11.3496 17.0307 11.3496C17.2747 11.3496 17.5087 11.2526 17.6807 11.0806C17.8532 10.9081 17.9502 10.6741 17.9502 10.4301V9.76911Z"
|
||||
fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_704_4275">
|
||||
<rect width="16" height="16" fill="white" transform="translate(3.7002 3.59961)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
12
src/assets/icons/room_avatar_placeholder.vue
Normal file
12
src/assets/icons/room_avatar_placeholder.vue
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M35.9999 30.4961L25.2856 49.0538H46.7142L35.9999 30.4961ZM35.9999 35.4448L29.5714 46.5794H42.4285L35.9999 35.4448Z"
|
||||
fill="#F4F952" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M35.9997 22.248L18.1426 53.1775H53.8569L35.9997 22.248ZM35.9997 27.1968L22.4283 50.7032H49.5711L35.9997 27.1968Z"
|
||||
fill="#F4F952" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M36 14L11 57.3013H61L36 14ZM36 18.9487L15.2857 54.8269H56.7143L36 18.9487Z" fill="#F4F952" />
|
||||
</svg>
|
||||
</template>
|
||||
21
src/assets/icons/room_type_channel.vue
Normal file
21
src/assets/icons/room_type_channel.vue
Normal file
File diff suppressed because one or more lines are too long
10
src/assets/icons/room_type_filedrop.vue
Normal file
10
src/assets/icons/room_type_filedrop.vue
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<svg width="27" height="26" viewBox="0 0 27 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M25.3037 15.9221C24.8876 15.9222 24.5503 16.2596 24.5503 16.6758V20.421H2.39235V16.6758C2.39235 16.2595 2.05496 15.9221 1.63867 15.9221C1.22242 15.9221 0.885 16.2595 0.885 16.6758V22.3857C0.885 23.8906 2.1093 25.1146 3.61446 25.1146H23.33C24.8342 25.1146 26.0577 23.8905 26.0577 22.3857V16.6758V16.6757C26.0573 16.2596 25.7203 15.9222 25.304 15.9221C25.3039 15.9221 25.3038 15.9221 25.3037 15.9221ZM23.3303 23.6073H3.61443C2.94038 23.6073 2.39231 23.0592 2.39229 22.3857C2.39229 22.3857 2.39229 22.3857 2.39229 22.3857L2.39238 21.9282H24.5506V22.3854C24.5506 22.3854 24.5506 22.3854 24.5506 22.3855C24.5503 23.0592 24.0029 23.6073 23.3303 23.6073Z"
|
||||
fill="black" stroke="black" stroke-width="0.23" />
|
||||
<path
|
||||
d="M20.9884 11.8001L20.9071 11.7188C21.1567 11.4692 21.1567 11.0648 20.9071 10.8155L20.9884 10.7342C20.9884 10.7342 20.9884 10.7341 20.9884 10.7341C20.6938 10.4396 20.2169 10.44 19.9227 10.7342L14.2263 16.4301V1.63867C14.2263 1.22242 13.8889 0.885 13.4726 0.885C13.0563 0.885 12.719 1.22239 12.719 1.63867V16.43L7.02366 10.7342L6.94234 10.8155L7.02365 10.7342C6.72909 10.4396 6.25212 10.44 5.95794 10.7342L5.95794 10.7342C5.66345 11.0287 5.66337 11.5059 5.958 11.8001L12.9395 18.7825L13.0209 18.7012L12.9395 18.7825C13.081 18.9239 13.2725 19.0032 13.4725 19.0032C13.6726 19.0032 13.8641 18.924 14.0055 18.7825L20.9884 11.8001Z"
|
||||
fill="black" stroke="black" stroke-width="0.23" />
|
||||
</svg>
|
||||
</template>
|
||||
9
src/assets/icons/room_type_room.vue
Normal file
9
src/assets/icons/room_type_room.vue
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<svg width="29" height="29" viewBox="0 0 29 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="10" y="11" width="18" height="13" rx="3" fill="currentColor" />
|
||||
<path d="M22.1029 28L14.2058 22.5L22 22.5L22.1029 28Z" fill="currentColor" />
|
||||
<path
|
||||
d="M25.0811 9.52329H19.6726V3.91892C19.6726 1.76351 17.9091 0 15.7537 0H3.91891C1.76351 0 0 1.76351 0 3.91892V11.4828C0 13.6382 1.76351 15.4017 3.91891 15.4017H5.68243V18.6929C5.68243 18.9669 5.83857 19.2409 6.07432 19.3588C6.19219 19.4369 6.31007 19.4767 6.46621 19.4767C6.62235 19.4767 6.74023 19.4369 6.8581 19.3588L9.28753 17.9091V21.0044C9.28753 23.1599 11.051 24.9234 13.2064 24.9234H15.518L22.1021 28.8821C22.22 28.9602 22.3761 29 22.494 29C22.6119 29 22.768 28.9602 22.8859 28.8821C23.1216 28.726 23.2778 28.4902 23.2778 28.2162V24.925H25.0811C27.2365 24.925 29 23.1615 29 21.006V13.4422C29 11.2868 27.2365 9.52329 25.0811 9.52329ZM7.24999 17.3213V14.6561C7.24999 14.2244 6.89791 13.8723 6.46621 13.8723H3.91891C2.62538 13.8723 1.56757 12.8145 1.56757 11.521V3.91895C1.56757 2.62541 2.62538 1.5676 3.91891 1.5676H15.7537C17.0472 1.5676 18.1051 2.62541 18.1051 3.91895V9.48349H13.2463C11.0909 9.48349 9.32734 11.247 9.32734 13.4024V16.0676L7.24999 17.3213ZM27.4324 21.006C27.4324 22.2996 26.3746 23.3574 25.0811 23.3574H22.5338C22.1021 23.3574 21.75 23.7095 21.75 24.1412V26.8063L16.1854 23.4753C16.0676 23.3972 15.9114 23.3574 15.7936 23.3574H13.2463C11.9527 23.3574 10.8949 22.2996 10.8949 21.006V13.4422C10.8949 12.1487 11.9527 11.0908 13.2463 11.0908H25.0811C26.3746 11.0908 27.4324 12.1487 27.4324 13.4422V21.006Z"
|
||||
fill="black" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -150,11 +150,12 @@
|
|||
"change": "Change"
|
||||
},
|
||||
"new_room": {
|
||||
"title": "New Group Chat",
|
||||
"new_room": "New Room",
|
||||
"create": "Create",
|
||||
"next": "Next",
|
||||
"name_room": "Room name",
|
||||
"room_topic": "Add a description if you like",
|
||||
"room_topic": "Add a description if you'd like",
|
||||
"join_permissions": "Join permissions",
|
||||
"set_join_permissions": "Set Join Permissions",
|
||||
"join_permissions_info": "These permissions determine how people can join the room and how easily others can be invited. They can be changed anytime.",
|
||||
|
|
@ -166,8 +167,7 @@
|
|||
"invite_info": "Only people added",
|
||||
"invite_description": "Choose from a list or search by account ID",
|
||||
"status_creating": "Creating room",
|
||||
"status_avatar_total": "Uploading avatar: {count} of {total}",
|
||||
"status_avatar": "Uploading avatar: {count}",
|
||||
"status_avatar": "Uploading avatar",
|
||||
"room_name_limit_error_msg": "Maximum 50 characters allowed",
|
||||
"colon_not_allowed": "Colon is not allowed",
|
||||
"options": "Options"
|
||||
|
|
@ -212,13 +212,32 @@
|
|||
"share_qr": "Share QR",
|
||||
"qr_image_copied": "Image copied to clipboard"
|
||||
},
|
||||
"create": {
|
||||
"title": "Choose an experience",
|
||||
"room_type_room_name": "Group Chat",
|
||||
"room_type_room_description": "Connect a group of people",
|
||||
"room_type_channel_name": "Channel",
|
||||
"room_type_channel_description": "Broadcast news and ideas",
|
||||
"room_type_filedrop_name": "File Drop",
|
||||
"room_type_filedrop_description": "Receive files and tips",
|
||||
"field_required": "This field is required",
|
||||
"topic_too_long": "Maximum 500 characters allowed"
|
||||
},
|
||||
"createchannel": {
|
||||
"title": "Create a Channel",
|
||||
"info": "Broadcast news or knowledge in any format—video, podcast, text, pictures or PDFs.",
|
||||
"channel_name": "Name your channel",
|
||||
"channel_topic": "Describe it",
|
||||
"name_required": "Channel name is required",
|
||||
"error_channel": "Failed to create channel"
|
||||
"channel_topic_label": "Your description will be displayed when people join your channel.",
|
||||
"error_channel": "Failed to create channel",
|
||||
"status_creating": "Creating channel"
|
||||
},
|
||||
"createfiledrop": {
|
||||
"title": "Create a File Drop",
|
||||
"info": "File drops are a safe space to receive files from anyone.",
|
||||
"filedrop_name": "Name your file drop",
|
||||
"error_filedrop": "Failed to create file drop",
|
||||
"status_creating": "Creating file drop"
|
||||
},
|
||||
"profile": {
|
||||
"title": "My Profile",
|
||||
|
|
@ -331,8 +350,9 @@
|
|||
"download_chat": "Download chat",
|
||||
"read_only_room": "Read Only",
|
||||
"read_only_room_info": "Only admins and moderators are allowed to send to the room.",
|
||||
"message_retention": "Message History",
|
||||
"message_retention_info": "Messages sent within this time frame are viewable by anyone with the link.",
|
||||
"message_retention": "Limit History",
|
||||
"message_retention_info": "Set a limit for how long to keep messages",
|
||||
"limit_history_info": "Keep messages for {period}",
|
||||
"message_retention_none": "Off",
|
||||
"message_retention_4_week": "4 weeks",
|
||||
"message_retention_2_week": "2 weeks",
|
||||
|
|
@ -340,12 +360,13 @@
|
|||
"message_retention_1_day": "1 day",
|
||||
"message_retention_8_hours": "8 hours",
|
||||
"message_retention_1_hour": "1 Hour",
|
||||
"make_public": "Make Public",
|
||||
"make_public_warning": "warning: Full message history will be visible to new participants",
|
||||
"direct_link": "My Direct Link",
|
||||
"direct_link_desc": "It's ready to share! A new direct room will open each time someone opens the link.",
|
||||
"shared_room_number": "You share {count} rooms with {name}",
|
||||
"shared_room_number_more": "You share more than {count} rooms with {name}"
|
||||
"shared_room_number_more": "You share more than {count} rooms with {name}",
|
||||
"message_history": "Message History",
|
||||
"message_history_info": "Allow people to see messages sent before they joined",
|
||||
"message_history_warning": "warning: Full message history will be visible to new participants"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"this_room": "This room",
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
v-on:remove-file="currentFileInputs.splice($event, 1)"
|
||||
v-on:reset="resetAttachments"
|
||||
:attachments="currentFileInputs"
|
||||
v-on:close="closeFileMode"
|
||||
/>
|
||||
|
||||
<div v-if="!useVoiceMode && !useFileModeNonAdmin" :class="{'chat-content': true, 'flex-grow-1': true, 'flex-shrink-1': true, 'invisible': !initialLoadDone}" ref="chatContainer"
|
||||
|
|
@ -699,8 +700,7 @@ export default {
|
|||
},
|
||||
useFileModeNonAdmin: {
|
||||
get: function() {
|
||||
if (!this.$config.experimental_file_mode) return false;
|
||||
return (util.roomDisplayTypeOverride(this.room) || this.roomDisplayType) === ROOM_TYPE_FILE_MODE && !this.canCreatePoll; // TODO - Check user or admin
|
||||
return this.roomDisplayType === ROOM_TYPE_FILE_MODE && !this.canCreatePoll; // TODO - Check user or admin
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -1556,6 +1556,7 @@ export default {
|
|||
const text = withText || "";
|
||||
const promise = this.sendAttachments(text, this.currentFileInputs);
|
||||
promise.then(() => {
|
||||
this.sendingAttachments = [];
|
||||
this.currentFileInputs = null;
|
||||
this.attachmentCaption = undefined;
|
||||
this.sendingStatus = this.sendStatuses.INITIAL;
|
||||
|
|
@ -2113,6 +2114,14 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
closeFileMode() {
|
||||
this.resetAttachments();
|
||||
this.$matrix.leaveRoomAndNavigate(this.room.roomId)
|
||||
.catch((err) => {
|
||||
console.log("Error leaving", err);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
135
src/components/Create.vue
Normal file
135
src/components/Create.vue
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<div class="create-root fill-height">
|
||||
<div class="mt-4 d-flex flex-row align-center">
|
||||
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
|
||||
</div>
|
||||
<div class="mt-40 text-center px-4">
|
||||
<div class="create-title no-icon h3-bold">{{ $t("create.title") }}</div>
|
||||
<div class="room-type" @click="createRoom" v-if="$config.roomTypes.includes('group_chat')">
|
||||
<div class="room-type__icon">
|
||||
<v-icon>$vuetify.icons.room_type_room</v-icon>
|
||||
</div>
|
||||
<div class="room-type__name">{{ $t("create.room_type_room_name") }}</div>
|
||||
<div class="room-type__description">{{ $t("create.room_type_room_description") }}</div>
|
||||
</div>
|
||||
<div class="room-type" @click="createChannel" v-if="$config.roomTypes.includes('channel')">
|
||||
<div class="room-type__icon">
|
||||
<v-icon>$vuetify.icons.room_type_channel</v-icon>
|
||||
</div>
|
||||
<div class="room-type__name">{{ $t("create.room_type_channel_name") }}</div>
|
||||
<div class="room-type__description">{{ $t("create.room_type_channel_description") }}</div>
|
||||
</div>
|
||||
<div class="room-type" @click="createFiledrop" v-if="$config.roomTypes.includes('file_drop')">
|
||||
<div class="room-type__icon">
|
||||
<v-icon>$vuetify.icons.room_type_filedrop</v-icon>
|
||||
</div>
|
||||
<div class="room-type__name">{{ $t("create.room_type_filedrop_name") }}</div>
|
||||
<div class="room-type__description">{{ $t("create.room_type_filedrop_description") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
|
||||
export default {
|
||||
name: "Create",
|
||||
components: { YouAre },
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
this.$navigation.pop();
|
||||
},
|
||||
createRoom() {
|
||||
this.$navigation.push({ name: "CreateRoom" });
|
||||
},
|
||||
createChannel() {
|
||||
this.$navigation.push({ name: "CreateChannel" });
|
||||
},
|
||||
createFiledrop() {
|
||||
this.$navigation.push({ name: "CreateFileDrop" });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/typography.scss";
|
||||
@import "@/assets/css/create.scss";
|
||||
|
||||
.create-title {
|
||||
margin-bottom: 24 * $chat-text-size;
|
||||
}
|
||||
|
||||
.room-type {
|
||||
padding: 15 * $chat-text-size;
|
||||
margin-bottom: 12 * $chat-text-size;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 8 * $chat-text-size;
|
||||
border: 1px solid #ededed;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #efeffe;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
border: 2px solid $very-very-purple;
|
||||
}
|
||||
|
||||
&:active::after {
|
||||
border: 1px solid $poll-hilite-color;
|
||||
}
|
||||
|
||||
.room-type__icon svg {
|
||||
color: transparent;
|
||||
}
|
||||
&:hover .room-type__icon svg, &:active .room-type__icon svg {
|
||||
color: $very-very-purple;
|
||||
}
|
||||
|
||||
.room-type__name {
|
||||
color: black;
|
||||
font-family: Inter;
|
||||
font-size: 16 * $chat-text-size;
|
||||
font-weight: 600;
|
||||
line-height: 18.72 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: center;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
margin-top: 8 * $chat-text-size;
|
||||
margin-bottom: 6 * $chat-text-size;
|
||||
}
|
||||
|
||||
.room-type__description {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-family: Inter;
|
||||
font-size: 13 * $chat-text-size;
|
||||
font-weight: 400;
|
||||
line-height: 15.21 * $chat-text-size;
|
||||
letter-spacing: 0.40 * $chat-text-size;
|
||||
text-align: center;
|
||||
text-underline-position: from-font;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,39 +1,46 @@
|
|||
<template>
|
||||
<div class="pa-4 create-root fill-height">
|
||||
<YouAre v-if="loggedIn" class="mt-4" />
|
||||
<div class="text-center">
|
||||
<v-avatar class="create-image-small" size="50" color="#ededed" @click.stop="showAvatarPicker">
|
||||
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
||||
<v-icon v-else>camera_alt</v-icon>
|
||||
</v-avatar>
|
||||
<div class="create-title">{{ $t("createchannel.title") }}</div>
|
||||
<div class="create-root fill-height">
|
||||
<div class="mt-4 d-flex flex-row align-center">
|
||||
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
|
||||
:disabled="creatingRoom">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
|
||||
</div>
|
||||
<div class="mt-40 px-4 text-center">
|
||||
<CreateRoomAvatar v-model="roomAvatar" />
|
||||
<div class="create-title h3-bold">{{ $t("createchannel.title") }}</div>
|
||||
<div class="create-info">{{ $t("createchannel.info") }}</div>
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<v-form v-model="isValid" ref="form">
|
||||
<v-text-field v-model="roomName" :label="$t('createchannel.channel_name')" color="black"
|
||||
background-color="white" solo :rules="[(v) => !!v || $t('createchannel.name_required')]" required
|
||||
v-on:keydown="message = null"></v-text-field>
|
||||
<v-text-field v-model="roomTopic" :label="$t('createchannel.channel_topic')" color="black"
|
||||
background-color="#F5F5F5" filled v-on:keydown="message = null"></v-text-field>
|
||||
|
||||
<!-- <div class="error--text" v-if="loadingLoginFlows">Loading login flows...</div> -->
|
||||
|
||||
<div class="error--text" v-if="message != null">{{ this.message }}</div>
|
||||
<v-form v-model="formIsValid">
|
||||
<v-text-field v-model="roomName" :label="$t('createchannel.channel_name')" v-bind="{ ...roomNameInputFields }"
|
||||
v-on="{ ...roomNameInputListeners }"></v-text-field>
|
||||
<v-text-field v-model="roomTopic" :label="$t('createchannel.channel_topic')"
|
||||
v-bind="{ ...roomTopicInputFields }" v-on="{ ...roomTopicInputListeners }"></v-text-field>
|
||||
<div class="caption-2 text-center mt-2 mb-8">{{ $t('createchannel.channel_topic_label') }}</div>
|
||||
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
|
||||
|
||||
<interactive-auth ref="interactiveAuth" />
|
||||
|
||||
<v-btn :disabled="!isValid || loading" color="primary" depressed block @click.stop="handleNext"
|
||||
:loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
|
||||
<v-btn v-if="!loggedIn" color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{
|
||||
$t("menu.login")
|
||||
}}</v-btn>
|
||||
<div class="create-buttons">
|
||||
<!-- Create button -->
|
||||
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="handleNext"
|
||||
class="filled-button mt-4">
|
||||
<div v-if="creatingRoom" class="text-center">
|
||||
{{ creatingRoomStatus }}
|
||||
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
|
||||
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
|
||||
</div>
|
||||
<span v-else>{{ $t("getlink.next") }}</span>
|
||||
</v-btn>
|
||||
<v-btn v-if="!loggedIn" color="black" depressed text @click.stop="goToLoginPage" class="text-button">{{
|
||||
$t("menu.login")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
|
||||
accept="image/*" class="d-none" />
|
||||
|
||||
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -47,14 +54,19 @@ import util, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
|
|||
import YouAre from "../components/YouAre.vue";
|
||||
import User from "../models/user";
|
||||
import { CHANNEL_POWER_LEVELS } from "../services/matrix.service";
|
||||
import createRoomMixin from "./createRoomMixin";
|
||||
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
|
||||
|
||||
export default {
|
||||
name: "CreateChannel",
|
||||
mixins: [rememberMeMixin, logoMixin],
|
||||
components: { InteractiveAuth, YouAre },
|
||||
mixins: [rememberMeMixin, logoMixin, createRoomMixin],
|
||||
components: { InteractiveAuth, YouAre, CreateRoomAvatar },
|
||||
data() {
|
||||
return this.defaultData();
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
|
|
@ -63,53 +75,80 @@ export default {
|
|||
return this.$store.state.auth.user;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
defaultData() {
|
||||
return {
|
||||
roomName: "",
|
||||
roomTopic: "",
|
||||
isValid: false,
|
||||
loading: false,
|
||||
message: null,
|
||||
currentLoginServer: "",
|
||||
loadingLoginFlows: false,
|
||||
loginFlows: null,
|
||||
showQRCopiedToast: false,
|
||||
roomAvatar: null,
|
||||
roomAvatarFile: null,
|
||||
};
|
||||
},
|
||||
goHome() {
|
||||
this.$navigation.push({ name: "Home" }, -1);
|
||||
},
|
||||
goToLoginPage() {
|
||||
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "GetLink" } }, 1);
|
||||
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "CreateChannel" } }, 1);
|
||||
},
|
||||
handleNext() {
|
||||
preCreateRoom() {
|
||||
const prefix = this.$config.userIdPrefix;
|
||||
const user = new User(util.randomUser(prefix), util.randomPass(), false);
|
||||
|
||||
// Reset errors
|
||||
this.message = null;
|
||||
this.loading = true;
|
||||
|
||||
const login = this.loggedIn ? Promise.resolve(this.currentUser) : this.loadLoginFlows().then(() => {
|
||||
return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler })
|
||||
});
|
||||
login
|
||||
.then(
|
||||
(ignoreduser) => {
|
||||
return this.createRoom();
|
||||
})
|
||||
.catch(
|
||||
(ignorederror) => {
|
||||
this.message = this.$t("createchannel.error_channel");
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
return login;
|
||||
},
|
||||
generateAliasForRoom() {
|
||||
return true;
|
||||
},
|
||||
getRoomCreationOptions() {
|
||||
const createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
name: this.roomName,
|
||||
preset: "public_chat",
|
||||
initial_state:
|
||||
[
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "m.room.retention",
|
||||
state_key: "",
|
||||
content: {
|
||||
max_lifetime: 3600 * 24 * 7 * 1000
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
if (this.roomTopic && this.roomTopic.length > 0) {
|
||||
// Add topic
|
||||
createRoomOptions.topic = this.roomTopic;
|
||||
}
|
||||
|
||||
createRoomOptions.creation_content = {
|
||||
type: ROOM_TYPE_CHANNEL
|
||||
}
|
||||
|
||||
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
|
||||
const powerLevels = {};
|
||||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: 50,
|
||||
state_default: 50,
|
||||
events: CHANNEL_POWER_LEVELS
|
||||
}
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: powerLevelContent
|
||||
});
|
||||
return createRoomOptions;
|
||||
},
|
||||
handleNext() {
|
||||
this.createRoom(this.$t("createchannel.status_creating"), this.$t("createchannel.error_channel"));
|
||||
},
|
||||
loadLoginFlows() {
|
||||
var user = Object.assign({}, this.user);
|
||||
|
|
@ -138,138 +177,11 @@ export default {
|
|||
supportedLoginFlow(flow) {
|
||||
return ["m.login.password"].includes(flow.type);
|
||||
},
|
||||
createRoom() {
|
||||
const createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
name: this.roomName,
|
||||
preset: "public_chat",
|
||||
initial_state:
|
||||
[
|
||||
// {
|
||||
// type: "m.room.encryption",
|
||||
// state_key: "",
|
||||
// content: {
|
||||
// algorithm: "m.megolm.v1.aes-sha2",
|
||||
// },
|
||||
// },
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "m.room.retention",
|
||||
state_key: "",
|
||||
content: {
|
||||
max_lifetime: 3600 * 24 * 7 * 1000
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
if (this.roomTopic && this.roomTopic.length > 0) {
|
||||
// Add topic
|
||||
createRoomOptions.topic = this.roomTopic;
|
||||
}
|
||||
|
||||
createRoomOptions.creation_content = {
|
||||
type: ROOM_TYPE_CHANNEL
|
||||
}
|
||||
|
||||
let roomId = "";
|
||||
|
||||
return util
|
||||
.getUniqueAliasForRoomName(
|
||||
this.$matrix.matrixClient,
|
||||
this.roomName,
|
||||
this.$matrix.currentUserMXDomain
|
||||
)
|
||||
.then((alias) => {
|
||||
createRoomOptions.room_alias_name = alias;
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
|
||||
const powerLevels = {};
|
||||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: 50,
|
||||
state_default: 50,
|
||||
events: CHANNEL_POWER_LEVELS
|
||||
}
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: powerLevelContent
|
||||
});
|
||||
|
||||
return this.$matrix.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
.then(({ room_id, room_alias }) => {
|
||||
roomId = room_alias || room_id;
|
||||
if (!this.roomAvatarFile) {
|
||||
return true;
|
||||
}
|
||||
const self = this;
|
||||
return util.setRoomAvatar(
|
||||
this.$matrix.matrixClient,
|
||||
room_id,
|
||||
this.roomAvatarFile,
|
||||
(p) => {
|
||||
if (p.total) {
|
||||
self.status = this.$t("new_room.status_avatar_total", {
|
||||
count: p.loaded || 0,
|
||||
total: p.total,
|
||||
});
|
||||
} else {
|
||||
self.status = this.$t("new_room.status_avatar", {
|
||||
count: p.loaded || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(roomId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Show picker to select room avatar file
|
||||
*/
|
||||
showAvatarPicker() {
|
||||
this.$refs.avatar.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle picked avatar
|
||||
*/
|
||||
handlePickedAvatar(event) {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.roomAvatar = e.target.result;
|
||||
this.roomAvatarFile = event.target.files[0];
|
||||
};
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/typography.scss";
|
||||
@import "@/assets/css/create.scss";
|
||||
</style>
|
||||
195
src/components/CreateFileDrop.vue
Normal file
195
src/components/CreateFileDrop.vue
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
<template>
|
||||
<div class="create-root fill-height">
|
||||
<div class="mt-4 d-flex flex-row align-center">
|
||||
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
|
||||
:disabled="creatingRoom">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
|
||||
</div>
|
||||
<div class="mt-40 px-4 text-center">
|
||||
<CreateRoomAvatar v-model="roomAvatar" class="v-hidden" />
|
||||
<div class="create-title h3-bold">{{ $t("createfiledrop.title") }}</div>
|
||||
<div class="create-info">{{ $t("createfiledrop.info") }}</div>
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<v-form v-model="formIsValid">
|
||||
<v-text-field v-model="roomName" :label="$t('createfiledrop.filedrop_name')"
|
||||
v-bind="{ ...roomNameInputFields }" v-on="{ ...roomNameInputListeners }"></v-text-field>
|
||||
|
||||
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
|
||||
|
||||
<interactive-auth ref="interactiveAuth" />
|
||||
|
||||
<div class="create-buttons">
|
||||
<!-- Create button -->
|
||||
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="handleNext"
|
||||
class="filled-button mt-4">
|
||||
<div v-if="creatingRoom" class="text-center">
|
||||
{{ creatingRoomStatus }}
|
||||
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
|
||||
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
|
||||
</div>
|
||||
<span v-else>{{ $t("getlink.next") }}</span>
|
||||
</v-btn>
|
||||
<v-btn v-if="!loggedIn" color="black" depressed text @click.stop="goToLoginPage" class="text-button">{{
|
||||
$t("menu.login")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import rememberMeMixin from "./rememberMeMixin";
|
||||
import * as sdk from "matrix-js-sdk";
|
||||
import logoMixin from "./logoMixin";
|
||||
import InteractiveAuth from './InteractiveAuth.vue';
|
||||
import util, { ROOM_TYPE_FILE_MODE } from "../plugins/utils";
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
import User from "../models/user";
|
||||
import createRoomMixin from "./createRoomMixin";
|
||||
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
|
||||
|
||||
export default {
|
||||
name: "CreateFileDrop",
|
||||
mixins: [rememberMeMixin, logoMixin, createRoomMixin],
|
||||
components: { InteractiveAuth, YouAre, CreateRoomAvatar },
|
||||
data() {
|
||||
return this.defaultData();
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
defaultData() {
|
||||
return {
|
||||
currentLoginServer: "",
|
||||
loadingLoginFlows: false,
|
||||
loginFlows: null,
|
||||
showQRCopiedToast: false,
|
||||
};
|
||||
},
|
||||
goToLoginPage() {
|
||||
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "CreateFileDrop" } }, 1);
|
||||
},
|
||||
preCreateRoom() {
|
||||
const prefix = this.$config.userIdPrefix;
|
||||
const user = new User(util.randomUser(prefix), util.randomPass(), false);
|
||||
const login = this.loggedIn ? Promise.resolve(this.currentUser) : this.loadLoginFlows().then(() => {
|
||||
return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler })
|
||||
});
|
||||
return login;
|
||||
},
|
||||
generateAliasForRoom() {
|
||||
return true;
|
||||
},
|
||||
getRoomCreationOptions() {
|
||||
const createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
name: this.roomName,
|
||||
preset: "private_chat",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.guest_access",
|
||||
state_key: "",
|
||||
content: {
|
||||
guest_access: "forbidden",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "joined"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "m.room.join_rules",
|
||||
state_key: "",
|
||||
content: {
|
||||
join_rule: "public"
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
if (this.roomTopic && this.roomTopic.length > 0) {
|
||||
// Add topic
|
||||
createRoomOptions.topic = this.roomTopic;
|
||||
}
|
||||
|
||||
createRoomOptions.creation_content = {
|
||||
type: ROOM_TYPE_FILE_MODE
|
||||
}
|
||||
|
||||
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
|
||||
const powerLevels = {};
|
||||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: 0,
|
||||
}
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: powerLevelContent
|
||||
});
|
||||
return createRoomOptions;
|
||||
},
|
||||
handleNext() {
|
||||
this.createRoom(this.$t("createfiledrop.status_creating"), this.$t("createfiledrop.error_filedrop"));
|
||||
},
|
||||
loadLoginFlows() {
|
||||
var user = Object.assign({}, this.user);
|
||||
return util.getMatrixBaseUrl(user, this.$config)
|
||||
.then((baseUrl) => {
|
||||
if (baseUrl !== this.currentLoginServer) {
|
||||
this.currentLoginServer = baseUrl;
|
||||
this.loadingLoginFlows = true;
|
||||
|
||||
const matrixClient = sdk.createClient({ baseUrl: baseUrl });
|
||||
return matrixClient.loginFlows().then((response) => {
|
||||
console.log("FLOWS", response.flows);
|
||||
this.loginFlows = response.flows.filter(this.supportedLoginFlow);
|
||||
this.loadingLoginFlows = false;
|
||||
if (this.loginFlows.length == 0) {
|
||||
this.message = this.$t('login.no_supported_flow')
|
||||
} else {
|
||||
this.message = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
})
|
||||
},
|
||||
supportedLoginFlow(flow) {
|
||||
return ["m.login.password"].includes(flow.type);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/typography.scss";
|
||||
@import "@/assets/css/create.scss";
|
||||
</style>
|
||||
|
|
@ -1,112 +1,71 @@
|
|||
<template>
|
||||
<div class="create-room fill-height">
|
||||
<div>
|
||||
<v-container fluid>
|
||||
<div class="room-name no-upper">{{ $t("new_room.new_room") }}</div>
|
||||
<v-btn id="btn-back" text class="header-button-left" v-show="$navigation && $navigation.canPop()"
|
||||
@click.stop="goBack" :disabled="step > steps.NAME_SET">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
<span class="d-none d-sm-block">{{ $t("menu.back") }}</span>
|
||||
</v-btn>
|
||||
<!-- <v-btn
|
||||
text
|
||||
:disabled="
|
||||
!roomName || (step != steps.INITIAL && step != steps.CREATED)
|
||||
"
|
||||
class="header-button-right"
|
||||
@click.stop="onCreate"
|
||||
>
|
||||
<span>{{
|
||||
step == steps.CREATED ? $t("new_room.done") : $t("new_room.next")
|
||||
}}</span>
|
||||
</v-btn> -->
|
||||
</v-container>
|
||||
<div class="create-root fill-height">
|
||||
<div class="mt-4 d-flex flex-row align-center">
|
||||
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
|
||||
:disabled="creatingRoom">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
|
||||
</div>
|
||||
<v-container fluid class="mt-40 px-4 text-center">
|
||||
<CreateRoomAvatar v-model="roomAvatar" />
|
||||
<div class="create-title h3-bold">{{ $t("new_room.title") }}</div>
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<v-form v-model="formIsValid">
|
||||
<v-text-field v-model="roomName" :label="$t('new_room.name_room')" v-bind="{ ...roomNameInputFields }"
|
||||
v-on="{ ...roomNameInputListeners }"></v-text-field>
|
||||
<v-text-field v-model="roomTopic" :label="$t('new_room.room_topic')" v-bind="{ ...roomTopicInputFields }"
|
||||
v-on="{ ...roomTopicInputListeners }"></v-text-field>
|
||||
|
||||
<v-container fluid class="mt-40">
|
||||
<v-row align="center">
|
||||
<v-col align="center">
|
||||
<v-avatar size="50" color="#ededed" @click.stop="showAvatarPicker">
|
||||
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
||||
<v-icon v-else>camera_alt</v-icon>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row cols="12" align="center" justify="center">
|
||||
<v-col sm="8" align="center">
|
||||
<div class="text-start font-weight-light">{{ $t("new_room.name_room") }}</div>
|
||||
<v-text-field v-model="roomName" color="black" :rules="roomNamerules" counter="50" maxlength="50"
|
||||
background-color="white" v-on:keyup.enter="$refs.topic.focus()" :disabled="step > steps.INITIAL" autofocus
|
||||
solo @update:error="updateErrorState"></v-text-field>
|
||||
<div class="text-start font-weight-light" v-show="roomName.length > 0">{{ $t("new_room.room_topic") }}</div>
|
||||
<v-textarea v-model="roomTopic" v-show="roomName.length > 0" ref="topic" color="black" background-color="white"
|
||||
v-on:keydown.enter.prevent="
|
||||
() => {
|
||||
$refs.create.$el.focus()
|
||||
}
|
||||
" :disabled="step > steps.INITIAL" solo full-width auto-grow rows="1" no-resize hide-details></v-textarea>
|
||||
<!-- Message History (public room) -->
|
||||
<v-card class="ma-0 mt-10" flat>
|
||||
<v-card-text class="with-right-label">
|
||||
<div>
|
||||
<div class="paragraph-bold">{{ $t('room_info.message_history') }}</div>
|
||||
<div class="caption-1-gray">{{ $t('room_info.message_history_info') }}</div>
|
||||
</div>
|
||||
<v-switch class="ma-0" v-model="publicRoom"></v-switch>
|
||||
</v-card-text>
|
||||
<div class="option-warning" v-if="publicRoom"><v-icon size="18">$vuetify.icons.ic_warning</v-icon>{{
|
||||
$t("room_info.message_history_warning") }}</div>
|
||||
</v-card>
|
||||
|
||||
<!-- Check if we have any options enabled in config -->
|
||||
<template v-if="$config.experimental_voice_mode || $config.experimental_read_only_room || $config.experimental_public_room || $config.experimental_file_mode">
|
||||
<div @click.stop="showOptions = !showOptions" v-show="roomName.length > 0" class="options clickable">
|
||||
<div>{{ $t("new_room.options") }}</div>
|
||||
<v-icon v-if="!showOptions">expand_more</v-icon>
|
||||
<v-icon v-else>expand_less</v-icon>
|
||||
<hr color="#ededed" />
|
||||
|
||||
<!-- Limit History (message retention) -->
|
||||
<v-card class="ma-0 mt-2" flat>
|
||||
<v-card-text class="with-right-label">
|
||||
<div>
|
||||
<div class="paragraph-bold">{{ $t('room_info.message_retention') }}</div>
|
||||
<div class="caption-1-gray">{{ limitHistory ? $t('room_info.limit_history_info', {period: roomMessageRetentionDisplayString(limitHistoryMilliseconds)}) : $t('room_info.message_retention_info') }}</div>
|
||||
<div v-show="limitHistory" @click="showRetentionDialog" class="paragraph paragraph-button mt-4 clickable">{{ $t('menu.edit') }}</div>
|
||||
</div>
|
||||
<v-switch class="ma-0" v-model="limitHistory"></v-switch>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
|
||||
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
|
||||
|
||||
<!-- Create button -->
|
||||
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="onCreate"
|
||||
class="filled-button mt-4">
|
||||
<div v-if="creatingRoom && !enterRoomDialog" class="text-center">
|
||||
{{ creatingRoomStatus }}
|
||||
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
|
||||
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
|
||||
</div>
|
||||
<v-card v-if="$config.experimental_public_room" v-show="showOptions" class="room-option account ma-0" flat>
|
||||
<v-card-text class="with-right-label">
|
||||
<div>
|
||||
<div class="option-title">{{ $t('room_info.make_public') }}</div>
|
||||
<!-- <div class="option-text">{{ $t('room_info.read_only_room_info') }}</div> -->
|
||||
</div>
|
||||
<v-switch v-model="unencryptedRoom"></v-switch>
|
||||
</v-card-text>
|
||||
<div class="option-warning" v-if="unencryptedRoom"><v-icon size="18">$vuetify.icons.ic_warning</v-icon>{{ $t("room_info.make_public_warning")}}</div>
|
||||
</v-card>
|
||||
|
||||
<v-card v-if="availableRoomTypes.length > 1" v-show="showOptions" class="room-option account ma-0" flat>
|
||||
<v-card-text>
|
||||
<div class="d-flex flex-wrap text-left">
|
||||
<div class="col-12 col-md-6 mr-auto pa-0">
|
||||
<div class="option-title">{{ $t('room_info.room_type') }}</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 pa-0">
|
||||
<RoomTypeSelector v-model="roomType" />
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card v-if="$config.experimental_read_only_room" v-show="showOptions" class="room-option account ma-0" flat>
|
||||
<v-card-text class="with-right-label">
|
||||
<div>
|
||||
<div class="option-title">{{ $t('room_info.read_only_room') }}</div>
|
||||
<div class="option-text">{{ $t('room_info.read_only_room_info') }}</div>
|
||||
</div>
|
||||
<v-switch v-model="readOnlyRoom"></v-switch>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<div class="error--text" v-if="roomCreationErrorMsg"> {{ roomCreationErrorMsg }}</div>
|
||||
<v-btn id="btn-room-create" color="black" depressed class="filled-button" @click.stop="onCreate"
|
||||
:disabled="isDisabled" ref="create">
|
||||
<div v-if="status && !enterRoomDialog" class="text-center">
|
||||
{{ status }}
|
||||
<v-progress-circular v-if="step == steps.CREATING" indeterminate color="primary"
|
||||
size="20"></v-progress-circular>
|
||||
</div>
|
||||
<span v-else>{{ $t("new_room.create") }}</span>
|
||||
<span v-else>{{ $t("getlink.next") }}</span>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</div>
|
||||
|
||||
</v-container>
|
||||
|
||||
<interactive-auth ref="interactiveAuth" />
|
||||
|
||||
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
|
||||
accept="image/*" class="d-none" />
|
||||
|
||||
<input id="user-avatar-picker" ref="useravatar" type="file" name="user-avatar" @change="handlePickedUserAvatar($event)" accept="image/*" class="d-none" />
|
||||
<input id="user-avatar-picker" ref="useravatar" type="file" name="user-avatar"
|
||||
@change="handlePickedUserAvatar($event)" accept="image/*" class="d-none" />
|
||||
|
||||
<v-dialog v-model="enterRoomDialog" :width="$vuetify.breakpoint.smAndUp ? '50%' : '90%'">
|
||||
<v-card>
|
||||
|
|
@ -117,9 +76,8 @@
|
|||
<v-select ref="avatar" :items="availableAvatars" cache-items outlined dense @change="selectAvatar"
|
||||
:value="selectedProfile">
|
||||
<template v-slot:selection>
|
||||
<v-text-field background-color="transparent" solo flat hide-details
|
||||
@click.native.stop="(event) => event.target.focus()"
|
||||
@focus="$event.target.select()"
|
||||
<v-text-field background-color="transparent" solo flat hide-details
|
||||
@click.native.stop="(event) => event.target.focus()" @focus="$event.target.select()"
|
||||
v-model="selectedProfile.name"></v-text-field>
|
||||
</template>
|
||||
<template v-slot:item="data">
|
||||
|
|
@ -149,54 +107,28 @@
|
|||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<MessageRetentionDialog :initialValue="limitHistoryMilliseconds" :show="selectRetentionDialog" v-on:close="selectRetentionDialog = false"
|
||||
v-on:message-retention-update="onUpdateHistoryLimit" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import util, { ROOM_TYPE_DEFAULT } from "../plugins/utils";
|
||||
import util from "../plugins/utils";
|
||||
import InteractiveAuth from './InteractiveAuth.vue';
|
||||
import rememberMeMixin from "./rememberMeMixin";
|
||||
import roomTypeMixin from "./roomTypeMixin";
|
||||
import RoomTypeSelector from './RoomTypeSelector.vue';
|
||||
|
||||
const steps = Object.freeze({
|
||||
INITIAL: 0,
|
||||
//NAME_SET: 1,
|
||||
CREATING: 2,
|
||||
CREATED: 3,
|
||||
});
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
import createRoomMixin from "./createRoomMixin";
|
||||
import roomRetentionMixin from "./roomRetentionMixin";
|
||||
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
|
||||
import MessageRetentionDialog from "./MessageRetentionDialog.vue";
|
||||
|
||||
export default {
|
||||
name: "CreateRoom",
|
||||
components: { InteractiveAuth, RoomTypeSelector },
|
||||
mixins: [rememberMeMixin, roomTypeMixin],
|
||||
components: { InteractiveAuth, YouAre, CreateRoomAvatar, MessageRetentionDialog },
|
||||
mixins: [rememberMeMixin, createRoomMixin, roomRetentionMixin],
|
||||
data() {
|
||||
return {
|
||||
steps,
|
||||
step: steps.INITIAL,
|
||||
roomId: null,
|
||||
roomName: "",
|
||||
roomTopic: "",
|
||||
roomAvatar: null,
|
||||
roomAvatarFile: null,
|
||||
status: "",
|
||||
joinRule: 0,
|
||||
joinRules: [
|
||||
{
|
||||
id: "public",
|
||||
text: this.$t("new_room.public_info"),
|
||||
icon: "link",
|
||||
descr: this.$t("new_room.public_description"),
|
||||
},
|
||||
{
|
||||
id: "invite",
|
||||
text: this.$t("new_room.invite_info"),
|
||||
icon: "person_add",
|
||||
descr: this.$t("new_room.invite_description"),
|
||||
},
|
||||
],
|
||||
publicRoomLink: null,
|
||||
publicRoomLinkCopied: false,
|
||||
availableAvatars: [],
|
||||
selectedProfile: {
|
||||
id: "",
|
||||
|
|
@ -204,21 +136,15 @@ export default {
|
|||
name: ""
|
||||
},
|
||||
enterRoomDialog: false,
|
||||
roomNamerules: [
|
||||
v => v.length <= 50 || this.$t("new_room.room_name_limit_error_msg"),
|
||||
v => !v.includes(':') || this.$t("new_room.colon_not_allowed")
|
||||
],
|
||||
roomNameHasError: false,
|
||||
roomCreationErrorMsg: "",
|
||||
showOptions: false,
|
||||
unencryptedRoom: false,
|
||||
readOnlyRoom: false,
|
||||
roomType: ROOM_TYPE_DEFAULT,
|
||||
selectRetentionDialog: false,
|
||||
publicRoom: false,
|
||||
limitHistory: false,
|
||||
limitHistoryMilliseconds: 2 * 7 * 24 * 3600 * 1000, // 2 weeks default
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.joinRule = this.joinRules[0].id; // Set default
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
this.availableAvatars = util.getDefaultAvatars();
|
||||
this.selectAvatar(
|
||||
this.availableAvatars[
|
||||
|
|
@ -227,21 +153,7 @@ export default {
|
|||
);
|
||||
},
|
||||
|
||||
watch: {
|
||||
joinRule() {
|
||||
console.log("Join rule changed to", this.joinRule);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isDisabled() {
|
||||
return this.status ? true : this.roomName.length === 0 || this.roomName.length > 50 || this.roomNameHasError
|
||||
},
|
||||
roomAvatarLetter() {
|
||||
if (!this.roomName) {
|
||||
return null;
|
||||
}
|
||||
return this.roomName.substring(0, 1).toUpperCase();
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
|
|
@ -255,17 +167,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
updateErrorState(errorState) {
|
||||
this.roomNameHasError = errorState
|
||||
},
|
||||
goBack() {
|
||||
if (this.step == steps.NAME_SET) {
|
||||
this.step = steps.INITIAL;
|
||||
} else {
|
||||
this.$navigation.pop();
|
||||
}
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
if (this.currentUser) {
|
||||
this.onEnterRoom();
|
||||
|
|
@ -276,105 +177,87 @@ export default {
|
|||
|
||||
onEnterRoom() {
|
||||
this.enterRoomDialog = false;
|
||||
if (this.step == steps.CREATED) {
|
||||
this.openRoom();
|
||||
} else if (this.step == steps.INITIAL) {
|
||||
// this.step = steps.NAME_SET;
|
||||
//} else if (this.step == steps.NAME_SET) {
|
||||
// Create room with deafult setting
|
||||
this.createRoom().then((roomId) => {
|
||||
this.roomId = roomId;
|
||||
this.openRoom(); // Open room (if id is set!)
|
||||
});
|
||||
}
|
||||
this.createRoom(this.$t("new_room.status_creating"), undefined);
|
||||
},
|
||||
|
||||
openRoom() {
|
||||
if (this.roomId) {
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(this.roomId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
getPublicLink() {
|
||||
this.createRoom().then((roomId) => {
|
||||
this.roomId = roomId;
|
||||
var room = null;
|
||||
if (roomId) {
|
||||
room = this.$matrix.getRoom(roomId);
|
||||
}
|
||||
if (room) {
|
||||
this.publicRoomLink = this.$router.getRoomLink(
|
||||
room.getCanonicalAlias(), roomId, room.name
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
addPeople() {
|
||||
// For now, jump straight to create
|
||||
this.createRoom().then((roomId) => {
|
||||
this.roomId = roomId;
|
||||
this.$matrix.setCurrentRoomId(roomId);
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Invite",
|
||||
},
|
||||
1
|
||||
);
|
||||
});
|
||||
},
|
||||
createRoom() {
|
||||
this.step = steps.CREATING;
|
||||
|
||||
preCreateRoom() {
|
||||
const hasUser = this.currentUser ? true : false;
|
||||
var setProfileData = false;
|
||||
|
||||
var roomId;
|
||||
this.status = this.$t("new_room.status_creating");
|
||||
return this.$matrix
|
||||
.getLoginPromise(this.$refs.interactiveAuth.registrationFlowHandler)
|
||||
.then((user) => {
|
||||
if (user.is_guest && !hasUser) {
|
||||
// Newly created account, joining first room.
|
||||
// Set avatar and display name to either the randomly chosen ones, or the
|
||||
// ones the users has changed to.
|
||||
setProfileData = true;
|
||||
|
||||
// Set display name and avatar directly on the matrix object.
|
||||
if (
|
||||
this.selectedProfile.name &&
|
||||
this.selectedProfile.name.length > 0
|
||||
) {
|
||||
this.$matrix.userDisplayName = this.selectedProfile.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!setProfileData ||
|
||||
!this.selectedProfile.name ||
|
||||
this.selectedProfile.name.length == 0
|
||||
) {
|
||||
return Promise.resolve(user);
|
||||
} else {
|
||||
console.log(
|
||||
"CreateRoom: Set display name to: " + this.selectedProfile.name
|
||||
);
|
||||
return this.$matrix.setUserDisplayName(this.selectedProfile.name);
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
if (!setProfileData || !this.selectedProfile.image) {
|
||||
console.log("CreateRoom: No avatar change");
|
||||
return Promise.resolve("no avatar");
|
||||
} else {
|
||||
console.log("CreateRoom: Updating avatar");
|
||||
return util.setAvatar(
|
||||
this.$matrix,
|
||||
this.selectedProfile.image,
|
||||
function (progress) {
|
||||
console.log("Progress: " + JSON.stringify(progress));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
generateAliasForRoom() {
|
||||
return this.publicRoom;
|
||||
},
|
||||
getRoomCreationOptions() {
|
||||
var createRoomOptions = {};
|
||||
if (this.joinRule == "public") {
|
||||
if (this.publicRoom) {
|
||||
createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
name: this.roomName,
|
||||
preset: "public_chat",
|
||||
initial_state:
|
||||
this.unencryptedRoom ? [
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "shared"
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "shared"
|
||||
}
|
||||
}
|
||||
}
|
||||
] :
|
||||
[
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "joined"
|
||||
}
|
||||
},
|
||||
],
|
||||
]
|
||||
};
|
||||
} else {
|
||||
//if (this.joinRule == "invite") {
|
||||
createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
name: this.roomName,
|
||||
preset: "private_chat",
|
||||
preset: "public_chat",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
|
|
@ -404,184 +287,32 @@ export default {
|
|||
// Add topic
|
||||
createRoomOptions.topic = this.roomTopic;
|
||||
}
|
||||
if (this.roomType != ROOM_TYPE_DEFAULT) {
|
||||
createRoomOptions.creation_content = {
|
||||
type: this.roomType
|
||||
}
|
||||
}
|
||||
|
||||
return this.$matrix
|
||||
.getLoginPromise(this.$refs.interactiveAuth.registrationFlowHandler)
|
||||
.then(
|
||||
function (user) {
|
||||
if (user.is_guest && !hasUser) {
|
||||
// Newly created account, joining first room.
|
||||
// Set avatar and display name to either the randomly chosen ones, or the
|
||||
// ones the users has changed to.
|
||||
setProfileData = true;
|
||||
|
||||
// Set display name and avatar directly on the matrix object.
|
||||
if (
|
||||
this.selectedProfile.name &&
|
||||
this.selectedProfile.name.length > 0
|
||||
) {
|
||||
this.$matrix.userDisplayName = this.selectedProfile.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!setProfileData ||
|
||||
!this.selectedProfile.name ||
|
||||
this.selectedProfile.name.length == 0
|
||||
) {
|
||||
return Promise.resolve(user);
|
||||
} else {
|
||||
console.log(
|
||||
"CreateRoom: Set display name to: " + this.selectedProfile.name
|
||||
);
|
||||
return this.$matrix.setUserDisplayName(this.selectedProfile.name);
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
if (!setProfileData || !this.selectedProfile.image) {
|
||||
console.log("CreateRoom: No avatar change");
|
||||
return Promise.resolve("no avatar");
|
||||
} else {
|
||||
console.log("CreateRoom: Updating avatar");
|
||||
return util.setAvatar(
|
||||
this.$matrix,
|
||||
this.selectedProfile.image,
|
||||
function (progress) {
|
||||
console.log("Progress: " + JSON.stringify(progress));
|
||||
}
|
||||
);
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
.then(() => {
|
||||
if (this.joinRule == "public") {
|
||||
// Promise to get a unique alias and use it in room creation options.
|
||||
//
|
||||
return util
|
||||
.getUniqueAliasForRoomName(
|
||||
this.$matrix.matrixClient,
|
||||
this.roomName,
|
||||
this.$matrix.currentUserMXDomain
|
||||
)
|
||||
.then((alias) => {
|
||||
createRoomOptions.room_alias_name = alias;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
// Limit history?
|
||||
if (this.limitHistory && this.limitHistoryMilliseconds > 0) {
|
||||
createRoomOptions.initial_state.push({
|
||||
type: "m.room.retention",
|
||||
state_key: "",
|
||||
content: {
|
||||
max_lifetime: this.limitHistoryMilliseconds
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
|
||||
const powerLevels = {};
|
||||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: this.readOnlyRoom ? 50 : 0
|
||||
}
|
||||
if (this.readOnlyRoom) {
|
||||
powerLevelContent.events = {
|
||||
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
|
||||
// power to 0 here. Otherwise we would not be able to send quick reactions or poll responses...
|
||||
"m.poll.response": 0,
|
||||
"org.matrix.msc3381.poll.response": 0,
|
||||
"m.reaction": 0,
|
||||
"m.room.redaction": 0,
|
||||
};
|
||||
}
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: powerLevelContent
|
||||
});
|
||||
}
|
||||
|
||||
return this.$matrix.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
.then(({ room_id, room_alias }) => {
|
||||
roomId = room_alias || room_id;
|
||||
if (!this.roomAvatarFile) {
|
||||
return true;
|
||||
}
|
||||
const self = this;
|
||||
return util.setRoomAvatar(
|
||||
this.$matrix.matrixClient,
|
||||
room_id,
|
||||
this.roomAvatarFile,
|
||||
(p) => {
|
||||
if (p.total) {
|
||||
self.status = this.$t("new_room.status_avatar_total", {
|
||||
count: p.loaded || 0,
|
||||
total: p.total,
|
||||
});
|
||||
} else {
|
||||
self.status = this.$t("new_room.status_avatar", {
|
||||
count: p.loaded || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.status = "";
|
||||
this.step = steps.CREATED;
|
||||
return roomId;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.status = ""
|
||||
this.roomCreationErrorMsg = (error.data && error.data.error) || error.message || error.toString();
|
||||
this.step = steps.INITIAL; // revert
|
||||
return null;
|
||||
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
|
||||
const powerLevels = {};
|
||||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: 0
|
||||
}
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: powerLevelContent
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show picker to select room avatar file
|
||||
*/
|
||||
showAvatarPicker() {
|
||||
if (this.step == steps.INITIAL) {
|
||||
this.$refs.avatar.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle picked avatar
|
||||
*/
|
||||
handlePickedAvatar(event) {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.roomAvatar = e.target.result;
|
||||
this.roomAvatarFile = event.target.files[0];
|
||||
};
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
}
|
||||
},
|
||||
|
||||
copyRoomLink() {
|
||||
const self = this;
|
||||
this.$copyText(this.publicRoomLink).then(
|
||||
function (ignored) {
|
||||
// Success!
|
||||
self.publicRoomLinkCopied = true;
|
||||
setInterval(() => {
|
||||
// Hide again
|
||||
self.publicRoomLinkCopied = false;
|
||||
}, 3000);
|
||||
},
|
||||
function (e) {
|
||||
// Failure!
|
||||
console.log(e);
|
||||
}
|
||||
);
|
||||
return createRoomOptions;
|
||||
},
|
||||
|
||||
selectAvatar(value) {
|
||||
|
|
@ -595,10 +326,8 @@ export default {
|
|||
/**
|
||||
* Show picker to select user avatar file
|
||||
*/
|
||||
showUserAvatarPicker() {
|
||||
if (this.step == steps.INITIAL) {
|
||||
this.$refs.useravatar.click();
|
||||
}
|
||||
showUserAvatarPicker() {
|
||||
this.$refs.useravatar.click();
|
||||
},
|
||||
|
||||
handlePickedUserAvatar(event) {
|
||||
|
|
@ -606,10 +335,22 @@ export default {
|
|||
this.selectedProfile.image = image;
|
||||
});
|
||||
},
|
||||
|
||||
showRetentionDialog() {
|
||||
this.selectRetentionDialog = true;
|
||||
},
|
||||
|
||||
onUpdateHistoryLimit(retention) {
|
||||
this.limitHistoryMilliseconds = retention;
|
||||
if (retention == 0) {
|
||||
this.limitHistory = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
@import "@/assets/css/create.scss";
|
||||
</style>
|
||||
|
|
@ -2,13 +2,14 @@
|
|||
<div class="pa-4 create-root fill-height">
|
||||
<div v-if="!loggedIn" class="text-center">
|
||||
<v-icon class="create-image">$vuetify.icons.getlink</v-icon>
|
||||
<div class="create-title">{{ $t("getlink.title") }}</div>
|
||||
<div class="create-title h3-bold">{{ $t("getlink.title") }}</div>
|
||||
<div class="create-info">{{ $t("getlink.info") }}</div>
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<v-form v-model="isValid" ref="form">
|
||||
<v-text-field v-model="user.user_id" :label="$t('getlink.username')" color="black" background-color="white" solo
|
||||
:rules="[(v) => !!v || $t('login.username_required')]" :error="userErrorMessage != null"
|
||||
:error-messages="userErrorMessage" required v-on:keyup.enter="onUsernameEnter" v-on:keydown="hasError = false"></v-text-field>
|
||||
<v-text-field v-model="user.user_id" :label="$t('getlink.username')" color="black" background-color="white"
|
||||
solo :rules="[(v) => !!v || $t('login.username_required')]" :error="userErrorMessage != null"
|
||||
:error-messages="userErrorMessage" required v-on:keyup.enter="onUsernameEnter"
|
||||
v-on:keydown="hasError = false"></v-text-field>
|
||||
|
||||
<!-- <div class="error--text" v-if="loadingLoginFlows">Loading login flows...</div> -->
|
||||
|
||||
|
|
@ -16,16 +17,19 @@
|
|||
|
||||
<interactive-auth ref="interactiveAuth" />
|
||||
|
||||
<v-btn id="btn-login" :disabled="!isValid || loading" color="primary" depressed block @click.stop="handleLogin"
|
||||
:loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{ $t("menu.login")
|
||||
}}</v-btn>
|
||||
<div class="create-buttons">
|
||||
<v-btn id="btn-login" :disabled="!isValid || loading" color="primary" depressed block
|
||||
@click.stop="handleLogin" :loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{
|
||||
$t("menu.login")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else style="position:relative" class="create-loggedin">
|
||||
<!-- Logged in/account created -->
|
||||
<div class="create-title">{{ $t("getlink.hello", { user: $matrix.currentUserDisplayName }) }}</div>
|
||||
<div class="create-title h3-bold">{{ $t("getlink.hello", { user: $matrix.currentUserDisplayName }) }}</div>
|
||||
<div class="create-subtitle">{{ $t("getlink.ready_to_share") }}</div>
|
||||
<copy-link ref="qr" :locationLink="directMessageLink" i18nQrPopupTitleKey="getlink.scan_title"
|
||||
v-on:long-tap="copyQRImage">
|
||||
|
|
@ -37,7 +41,8 @@
|
|||
</template>
|
||||
</copy-link>
|
||||
<div class="create-buttons">
|
||||
<v-btn color="black" depressed @click.stop="goHome" class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
|
||||
<v-btn color="black" style="color:black;background-color:white !important;" depressed @click.stop="goHome"
|
||||
class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="getDifferentLink" class="text-button">{{
|
||||
$t("getlink.different_link") }}</v-btn>
|
||||
</div>
|
||||
|
|
@ -243,5 +248,6 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/typography.scss";
|
||||
@import "@/assets/css/create.scss";
|
||||
</style>
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div class="home">
|
||||
<YouAre class="mt-4" />
|
||||
<div class="mt-4 d-flex flex-row align-center">
|
||||
<YouAre class="flex-grow-1 text-end" />
|
||||
</div>
|
||||
|
||||
<v-container fluid class="text-center mt-8">
|
||||
<v-row align="center" justify="center">
|
||||
<v-col class="text-center" cols="auto">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<v-dialog v-model="showDialog" v-show="room" class="ma-0 pa-0"
|
||||
:width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'">
|
||||
<v-dialog v-model="showDialog" v-show="room" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'">
|
||||
<div class="dialog-content text-center">
|
||||
<template>
|
||||
<h2>{{ $t("room_info.message_retention") }}</h2>
|
||||
|
|
@ -10,8 +9,8 @@
|
|||
<v-col cols="12">
|
||||
<v-form v-model="isValid">
|
||||
<v-radio-group v-model="retention" class="my-0 py-0">
|
||||
<v-radio active-class="radio-active" class="flex-row-reverse mb-0" v-for="p in retentionPeriods" :key="p.text" :label="p.text"
|
||||
:value="p.value" />
|
||||
<v-radio active-class="radio-active" class="flex-row-reverse mb-0" v-for="p in retentionPeriods"
|
||||
:key="p.text" :label="p.text" :value="p.value" />
|
||||
</v-radio-group>
|
||||
</v-form>
|
||||
</v-col>
|
||||
|
|
@ -40,6 +39,12 @@ export default {
|
|||
return false;
|
||||
},
|
||||
},
|
||||
initialValue: {
|
||||
type: Number,
|
||||
default: function () {
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -71,23 +76,29 @@ export default {
|
|||
methods: {
|
||||
setInitialValue() {
|
||||
this.isValid = true;
|
||||
this.retention = this.roomMessageRetention();
|
||||
if (this.room) {
|
||||
this.retention = this.roomMessageRetention();
|
||||
} else {
|
||||
this.retention = this.initialValue;
|
||||
}
|
||||
},
|
||||
setRetention() {
|
||||
let ms = this.retention;
|
||||
if (ms === 0 || !ms) {
|
||||
// No expiry
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: 0 }
|
||||
);
|
||||
} else if (ms >= 1000 * this.retentionMinValue && ms <= 1000 * this.retentionMaxValue) {
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: ms }
|
||||
);
|
||||
if (this.room) {
|
||||
if (ms === 0 || !ms) {
|
||||
// No expiry
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: 0 }
|
||||
);
|
||||
} else if (ms >= 1000 * this.retentionMinValue && ms <= 1000 * this.retentionMaxValue) {
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: ms }
|
||||
);
|
||||
}
|
||||
}
|
||||
this.$emit('message-retention-update', ms)
|
||||
this.showDialog = false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<div>
|
||||
<v-chip
|
||||
@click="viewProfile"
|
||||
class="ma-2 white-space-pre"
|
||||
|
|
|
|||
78
src/components/create/CreateRoomAvatar.vue
Normal file
78
src/components/create/CreateRoomAvatar.vue
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<div class="create-room-avatar" color="#ededed">
|
||||
<v-img class="create-room-avatar__icon clickable" @click.stop="showRoomAvatarPicker" v-if="modelValue && modelValue.image" :src="modelValue.image" />
|
||||
<v-icon class="create-room-avatar__icon default" v-else>$vuetify.icons.room_avatar_placeholder</v-icon>
|
||||
<v-icon class="create-room-avatar__camera clickable" v-if="!modelValue || !modelValue.image" @click.stop="showRoomAvatarPicker">$vuetify.icons.ic_camera</v-icon>
|
||||
<input id="room-avatar-picker" ref="roomAvatar" type="file" name="roomAvatar"
|
||||
@change="handlePickedRoomAvatar($event)" accept="image/*" class="d-none" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CreateRoomAvatar",
|
||||
model: {
|
||||
prop: "modelValue",
|
||||
event: "update:modelValue",
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Show picker to select room avatar file
|
||||
*/
|
||||
showRoomAvatarPicker() {
|
||||
this.$refs.roomAvatar.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle picked avatar
|
||||
*/
|
||||
handlePickedRoomAvatar(event) {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.$emit('update:modelValue', {
|
||||
image: e.target.result,
|
||||
file: event.target.files[0]
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/create.scss";
|
||||
|
||||
.create-room-avatar {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
.create-room-avatar__icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 14px;
|
||||
&.default {
|
||||
background: $light-purple;
|
||||
}
|
||||
}
|
||||
|
||||
.create-room-avatar__camera {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
155
src/components/createRoomMixin.js
Normal file
155
src/components/createRoomMixin.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import util from "../plugins/utils";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
creatingRoom: false,
|
||||
creatingRoomStatus: null,
|
||||
creatingRoomProgress: null,
|
||||
formIsValid: false,
|
||||
roomName: "",
|
||||
roomTopic: "",
|
||||
roomAvatar: null,
|
||||
roomNameRules: [
|
||||
(v) => !!v || this.$t("create.field_required"),
|
||||
(v) => !v || v.length <= 50 || this.$t("new_room.room_name_limit_error_msg"),
|
||||
(v) => !v || !v.includes(":") || this.$t("new_room.colon_not_allowed"),
|
||||
],
|
||||
roomTopicRules: [(v) => !v || v.length <= 500 || this.$t("create.topic_too_long")],
|
||||
errorMessage: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
roomNameInputFields() {
|
||||
return {
|
||||
color: "black",
|
||||
rules: this.roomNameRules,
|
||||
counter: "50",
|
||||
maxLength: "50",
|
||||
"background-color": "white",
|
||||
disabled: this.creatingRoom,
|
||||
//autofocus: true,
|
||||
solo: true,
|
||||
required: true,
|
||||
};
|
||||
},
|
||||
roomNameInputListeners() {
|
||||
return {
|
||||
keydown: () => (this.errorMessage = null),
|
||||
keyup: (e) => {
|
||||
if (e.keyCode === 13 && this.$refs.topic) {
|
||||
this.$refs.topic.focus();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
roomTopicInputFields() {
|
||||
return {
|
||||
ref: "topic",
|
||||
color: "black",
|
||||
rules: this.roomTopicRules,
|
||||
maxLength: "500",
|
||||
disabled: this.creatingRoom,
|
||||
filled: true,
|
||||
rounded: true,
|
||||
"hide-details": true,
|
||||
};
|
||||
},
|
||||
roomTopicInputListeners() {
|
||||
return {
|
||||
keydown: () => (this.errorMessage = null),
|
||||
keyup: (e) => {
|
||||
if (e.keyCode === 13 && this.$refs.create) {
|
||||
this.$refs.create.focus();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
if (!this.creatingRoom) {
|
||||
this.$navigation.pop();
|
||||
}
|
||||
},
|
||||
createRoom(creatingMessageString, errorMessageString) {
|
||||
// Reset errors
|
||||
this.errorMessage = null;
|
||||
this.creatingRoom = true;
|
||||
this.creatingRoomStatus = creatingMessageString;
|
||||
this.creatingRoomProgress = null;
|
||||
|
||||
let roomId = "";
|
||||
|
||||
this.preCreateRoom()
|
||||
.then(() => {
|
||||
let createRoomOptions = this.getRoomCreationOptions();
|
||||
if (this.generateAliasForRoom()) {
|
||||
// Promise to get a unique alias and use it in room creation options.
|
||||
//
|
||||
return util
|
||||
.getUniqueAliasForRoomName(
|
||||
this.$matrix.matrixClient,
|
||||
this.roomName,
|
||||
this.$matrix.currentUserMXDomain
|
||||
)
|
||||
.then((alias) => {
|
||||
createRoomOptions.room_alias_name = alias;
|
||||
return createRoomOptions;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(createRoomOptions);
|
||||
}
|
||||
})
|
||||
.then((createRoomOptions) => {
|
||||
return this.$matrix.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
.then(({ room_id }) => {
|
||||
roomId = util.sanitizeRoomId(room_id);
|
||||
if (createRoomOptions.room_alias_name) {
|
||||
const parts = roomId.split(":");
|
||||
if (parts && parts.length == 2) {
|
||||
roomId = "#" + createRoomOptions.room_alias_name + ":" + parts[1];
|
||||
}
|
||||
}
|
||||
if (!this.roomAvatar || !this.roomAvatar.file) {
|
||||
return true;
|
||||
}
|
||||
return util.setRoomAvatar(
|
||||
this.$matrix.matrixClient,
|
||||
room_id,
|
||||
this.roomAvatar.file,
|
||||
(p) => {
|
||||
this.creatingRoomStatus = this.$t("new_room.status_avatar");
|
||||
if (p.total) {
|
||||
this.creatingRoomProgress = (100 * p.loaded) / p.total;
|
||||
} else {
|
||||
this.creatingRoomProgress = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.creatingRoomProgress = null;
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: roomId },
|
||||
},
|
||||
-1
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.errorMessage = errorMessageString || (error.data && error.data.error) || error.message || error.toString();
|
||||
})
|
||||
.finally(() => {
|
||||
this.creatingRoom = false;
|
||||
this.creatingRoomStatus = null;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -192,10 +192,7 @@ export default {
|
|||
this.currentItemIndex = 0;
|
||||
},
|
||||
close() {
|
||||
this.$matrix.leaveRoomAndNavigate(this.room.roomId)
|
||||
.catch((err) => {
|
||||
console.log("Error leaving", err);
|
||||
});
|
||||
this.$emit('close');
|
||||
},
|
||||
sendAll() {
|
||||
this.status = this.mainStatuses.SENDING;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
<script>
|
||||
import MessageIncoming from "./MessageIncoming.vue";
|
||||
import messageMixin from "./messageMixin";
|
||||
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
|
||||
import util, { ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE } from "../../plugins/utils";
|
||||
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
|
||||
import ThumbnailView from '../file_mode/ThumbnailView.vue';
|
||||
import SwipeableThumbnailsView from "./channel/SwipeableThumbnailsView.vue";
|
||||
|
|
@ -66,7 +66,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
forceMultiview() {
|
||||
return this.room.displayType == ROOM_TYPE_CHANNEL && this.items.length == 1 && util.isFileTypePDF(this.items[0].event);
|
||||
return this.room.displayType == ROOM_TYPE_FILE_MODE || (this.room.displayType == ROOM_TYPE_CHANNEL && this.items.length == 1 && util.isFileTypePDF(this.items[0].event));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import utils, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
|
||||
import roomTypeMixin from "./roomTypeMixin";
|
||||
import roomRetentionMixin from "./roomRetentionMixin";
|
||||
|
||||
export default {
|
||||
mixins: [roomTypeMixin],
|
||||
mixins: [roomTypeMixin, roomRetentionMixin],
|
||||
data() {
|
||||
return {
|
||||
roomJoinRule: null,
|
||||
|
|
@ -15,36 +16,6 @@ export default {
|
|||
isRoomTopicEditMode: false,
|
||||
roomTopicErrorMessage: null,
|
||||
messageRetentionDisplay: "",
|
||||
retentionPeriods: [
|
||||
{
|
||||
text: this.$t("room_info.message_retention_none"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_4_week"),
|
||||
value: 3600 * 24 * 28 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_2_week"),
|
||||
value: 3600 * 24 * 14 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_week"),
|
||||
value: 3600 * 24 * 7 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_day"),
|
||||
value: 3600 * 24 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_8_hours"),
|
||||
value: 3600 * 8 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_hour"),
|
||||
value: 3600 * 1000
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
48
src/components/roomRetentionMixin.js
Normal file
48
src/components/roomRetentionMixin.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
retentionPeriods: [
|
||||
{
|
||||
text: this.$t("room_info.message_retention_none"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_4_week"),
|
||||
value: 3600 * 24 * 28 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_2_week"),
|
||||
value: 3600 * 24 * 14 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_week"),
|
||||
value: 3600 * 24 * 7 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_day"),
|
||||
value: 3600 * 24 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_8_hours"),
|
||||
value: 3600 * 8 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_hour"),
|
||||
value: 3600 * 1000
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Get a string describing current room retention setting.
|
||||
* Can be "None", "1 week", "1 hour" etc...
|
||||
*/
|
||||
roomMessageRetentionDisplayString(milliseconds) {
|
||||
const retentionPeriodFound = this.retentionPeriods.find(rp=>rp.value===milliseconds)
|
||||
if(retentionPeriodFound) {
|
||||
return retentionPeriodFound.text
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -123,7 +123,6 @@ export default {
|
|||
})
|
||||
.then(() => {
|
||||
this.sendingStatus = this.sendStatuses.SENT;
|
||||
this.sendingAttachments = [];
|
||||
this.sendingRootEventId = null;
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ import Login from '../components/Login.vue'
|
|||
import Profile from '../components/Profile.vue'
|
||||
import CreateRoom from '../components/CreateRoom.vue'
|
||||
import GetLink from '../components/GetLink.vue'
|
||||
import Create from '../components/Create.vue'
|
||||
import CreateChannel from '../components/CreateChannel.vue'
|
||||
import CreateFileDrop from '../components/CreateFileDrop.vue'
|
||||
import User from '../models/user'
|
||||
import util from '../plugins/utils'
|
||||
|
||||
|
|
@ -61,11 +63,21 @@ const routes = [
|
|||
name: 'GetLink',
|
||||
component: GetLink,
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
name: 'Create',
|
||||
component: Create,
|
||||
},
|
||||
{
|
||||
path: '/createchannel',
|
||||
name: 'CreateChannel',
|
||||
component: CreateChannel,
|
||||
},
|
||||
{
|
||||
path: '/createfiledrop',
|
||||
name: 'CreateFileDrop',
|
||||
component: CreateFileDrop,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
|
|
@ -103,7 +115,7 @@ const router = new VueRouter({
|
|||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const publicPages = ['/login', '/createroom', '/getlink', '/createchannel'];
|
||||
const publicPages = ['/login', '/createroom', '/getlink', '/create', '/createchannel', '/createfiledrop'];
|
||||
var authRequired = !publicPages.includes(to.path);
|
||||
const loggedIn = router.app.$store.state.auth.user;
|
||||
|
||||
|
|
@ -130,6 +142,11 @@ router.beforeEach((to, from, next) => {
|
|||
//Invite to public room
|
||||
authRequired = false;
|
||||
}
|
||||
} else if (to.name == 'Home') {
|
||||
if (to.params.roomId !== undefined) {
|
||||
const roomId = to.params.roomId ? util.sanitizeRoomId(to.params.roomId) : null;
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
}
|
||||
} else if (to.name == 'User') {
|
||||
if (to.params.userId) {
|
||||
let roomId = util.sanitizeUserId(to.params.userId);
|
||||
|
|
@ -153,7 +170,23 @@ router.beforeEach((to, from, next) => {
|
|||
}
|
||||
} else if (to.name == 'CreateRoom') {
|
||||
return router.app.$config.promise.then((config) => {
|
||||
if (config.hide_add_room_on_home) {
|
||||
if (config.hide_add_room_on_home || !config.roomTypes.includes("group_chat")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}).catch(err => { console.error(err); next('/'); });
|
||||
} else if (to.name == 'CreateChannel') {
|
||||
return router.app.$config.promise.then((config) => {
|
||||
if (!config.roomTypes.includes("channel")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}).catch(err => { console.error(err); next('/'); });
|
||||
} else if (to.name == 'CreateFileDrop') {
|
||||
return router.app.$config.promise.then((config) => {
|
||||
if (!config.roomTypes.includes("file_drop")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
|
|
|
|||
|
|
@ -29,8 +29,18 @@ export default {
|
|||
if (!json.maxSizeAutoDownloads) {
|
||||
Vue.set(config, "maxSizeAutoDownloads", 10 * 1024 * 1024);
|
||||
}
|
||||
if (!json.roomTypes) {
|
||||
let roomTypes = ["group_chat", "channel"];
|
||||
const fileDropEnabled = (json.experimental_file_mode === undefined) ? true : !!json.experimental_file_mode;
|
||||
if (fileDropEnabled) {
|
||||
roomTypes.push("file_drop");
|
||||
}
|
||||
json.roomTypes = roomTypes;
|
||||
}
|
||||
Vue.set(config, "loaded", true);
|
||||
|
||||
console.warn("ROOM TYPES CONFIG", json.roomTypes);
|
||||
|
||||
// Tell callback we are done loading runtime config
|
||||
if (onloaded) {
|
||||
onloaded(config);
|
||||
|
|
|
|||
|
|
@ -417,6 +417,14 @@ export default {
|
|||
}
|
||||
break;
|
||||
|
||||
case "m.room.canonical_alias":
|
||||
{
|
||||
if (this.currentRoomId && this.currentRoomId.startsWith("#") && !this.currentRoom) {
|
||||
this.currentRoom = this.getRoom(this.currentRoomId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_EVENT_ROOM_DELETED:
|
||||
{
|
||||
const room = this.matrixClient.getRoom(event.getRoomId());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue