2020-07-09 17:24:28 +02:00
< template >
< div >
2022-01-10 15:19:16 +01:00
< breadcrumbs-nav
v - if = "group"
: links = " [
{
name : RouteName . GROUP ,
params : { preferredUsername : usernameWithDomain ( group ) } ,
text : displayName ( group ) ,
} ,
{
name : RouteName . GROUP _SETTINGS ,
params : { preferredUsername : usernameWithDomain ( group ) } ,
2022-08-26 16:08:58 +02:00
text : t ( 'Settings' ) ,
2022-01-10 15:19:16 +01:00
} ,
{
name : RouteName . GROUP _PUBLIC _SETTINGS ,
params : { preferredUsername : usernameWithDomain ( group ) } ,
2022-08-26 16:08:58 +02:00
text : t ( 'Group settings' ) ,
2022-01-10 15:19:16 +01:00
} ,
] "
/ >
2022-07-12 10:55:28 +02:00
< o-loading :active = "loading" / >
2020-11-30 10:24:11 +01:00
< section
2022-08-26 16:08:58 +02:00
class = "container mx-auto mb-6"
2020-11-30 10:24:11 +01:00
v - if = "group && isCurrentActorAGroupAdmin"
>
2022-08-26 16:08:58 +02:00
< form @submit.prevent ="updateGroup(buildVariables)" v-if = "editableGroup" >
< o-field : label = "t('Group name')" label -for = " group -settings -name " >
2022-07-12 10:55:28 +02:00
< o-input v-model = "editableGroup.name" id="group-settings-name" / >
< / o-field >
2022-08-26 16:08:58 +02:00
< o-field : label = "t('Group short description')" >
2022-07-12 10:55:28 +02:00
< Editor
2021-10-10 16:24:12 +02:00
mode = "basic"
v - model = "editableGroup.summary"
: maxSize = "500"
2022-08-26 16:08:58 +02:00
: aria - label = "t('Group description body')"
2022-07-12 10:55:28 +02:00
v - if = "currentActor"
: currentActor = "currentActor"
/ > < / o - f i e l d >
2022-08-26 16:08:58 +02:00
< o-field :label = "t('Avatar')" >
2020-09-29 09:53:48 +02:00
< picture-upload
2022-08-26 16:08:58 +02:00
: textFallback = "t('Avatar')"
2020-09-29 09:53:48 +02:00
v - model = "avatarFile"
2020-11-20 18:34:13 +01:00
: defaultImage = "group.avatar"
2021-04-12 10:43:04 +02:00
: maxSize = "avatarMaxSize"
2020-09-29 09:53:48 +02:00
/ >
2022-07-12 10:55:28 +02:00
< / o-field >
2020-09-29 09:53:48 +02:00
2022-08-26 16:08:58 +02:00
< o-field :label = "t('Banner')" >
2020-09-29 09:53:48 +02:00
< picture-upload
2022-08-26 16:08:58 +02:00
: textFallback = "t('Banner')"
2020-09-29 09:53:48 +02:00
v - model = "bannerFile"
2020-11-20 18:34:13 +01:00
: defaultImage = "group.banner"
2021-04-12 10:43:04 +02:00
: maxSize = "bannerMaxSize"
2020-09-29 09:53:48 +02:00
/ >
2022-07-12 10:55:28 +02:00
< / o-field >
2022-08-26 16:08:58 +02:00
< p class = "label" > { { t ( "Group visibility" ) } } < / p >
2020-08-05 16:44:08 +02:00
< div class = "field" >
2022-07-12 10:55:28 +02:00
< o-radio
2021-06-15 17:25:33 +02:00
v - model = "editableGroup.visibility"
2020-08-05 16:44:08 +02:00
name = "groupVisibility"
: native - value = "GroupVisibility.PUBLIC"
>
2022-08-26 16:08:58 +02:00
{ { t ( "Visible everywhere on the web" ) } } < br / >
2020-08-05 16:44:08 +02:00
< small > { {
2022-08-26 16:08:58 +02:00
t (
2020-08-05 16:44:08 +02:00
"The group will be publicly listed in search results and may be suggested in the explore section. Only public informations will be shown on it's page."
)
} } < / small >
2022-07-12 10:55:28 +02:00
< / o-radio >
2020-08-05 16:44:08 +02:00
< / div >
< div class = "field" >
2022-07-12 10:55:28 +02:00
< o-radio
2021-06-15 17:25:33 +02:00
v - model = "editableGroup.visibility"
2020-08-05 16:44:08 +02:00
name = "groupVisibility"
2021-06-16 11:25:53 +02:00
: native - value = "GroupVisibility.UNLISTED"
2022-08-26 16:08:58 +02:00
> { { t ( "Only accessible through link" ) } } < br / >
2020-08-05 16:44:08 +02:00
< small > { {
2022-08-26 16:08:58 +02:00
t (
2020-09-02 10:00:39 +02:00
"You'll need to transmit the group URL so people may access the group's profile. The group won't be findable in Mobilizon's search or regular search engines."
)
2020-08-05 16:44:08 +02:00
} } < / small >
2022-07-12 10:55:28 +02:00
< / o-radio >
< p class = "pl-6" >
2020-08-05 16:44:08 +02:00
< code > { { group . url } } < / code >
2022-07-12 10:55:28 +02:00
< o-tooltip
2020-08-05 16:44:08 +02:00
v - if = "canShowCopyButton"
2022-08-26 16:08:58 +02:00
: label = "t('URL copied to clipboard')"
2020-08-05 16:44:08 +02:00
: active = "showCopiedTooltip"
always
2022-07-12 10:55:28 +02:00
variant = "success"
position = "left"
2020-08-05 16:44:08 +02:00
>
2022-07-12 10:55:28 +02:00
< o-button
variant = "primary"
2020-08-05 16:44:08 +02:00
icon - right = "content-paste"
native - type = "button"
@ click = "copyURL"
@ keyup . enter = "copyURL"
/ >
2022-07-12 10:55:28 +02:00
< / o-tooltip >
2020-08-05 16:44:08 +02:00
< / p >
< / div >
2022-08-26 16:08:58 +02:00
< p class = "label" > { { t ( "New members" ) } } < / p >
2020-11-06 11:34:32 +01:00
< div class = "field" >
2022-07-12 10:55:28 +02:00
< o-radio
2021-06-15 17:25:33 +02:00
v - model = "editableGroup.openness"
2020-11-30 10:24:11 +01:00
name = "groupOpenness"
: native - value = "Openness.OPEN"
>
2022-08-26 16:08:58 +02:00
{ { t ( "Anyone can join freely" ) } } < br / >
2020-11-06 11:34:32 +01:00
< small > { {
2022-08-26 16:08:58 +02:00
t (
2020-11-06 11:34:32 +01:00
"Anyone wanting to be a member from your group will be able to from your group page."
)
} } < / small >
2022-07-12 10:55:28 +02:00
< / o-radio >
2020-11-06 11:34:32 +01:00
< / div >
2021-11-12 15:42:52 +01:00
< div class = "field" >
2022-07-12 10:55:28 +02:00
< o-radio
2021-11-12 15:42:52 +01:00
v - model = "editableGroup.openness"
name = "groupOpenness"
: native - value = "Openness.MODERATED"
2022-08-26 16:08:58 +02:00
> { { t ( "Moderate new members" ) } } < br / >
2021-11-12 15:42:52 +01:00
< small > { {
2022-08-26 16:08:58 +02:00
t (
2021-11-12 15:42:52 +01:00
"Anyone can request being a member, but an administrator needs to approve the membership."
)
} } < / small >
2022-07-12 10:55:28 +02:00
< / o-radio >
2021-11-12 15:42:52 +01:00
< / div >
2020-11-06 11:34:32 +01:00
< div class = "field" >
2022-07-12 10:55:28 +02:00
< o-radio
2021-06-15 17:25:33 +02:00
v - model = "editableGroup.openness"
2020-11-06 11:34:32 +01:00
name = "groupOpenness"
: native - value = "Openness.INVITE_ONLY"
2022-08-26 16:08:58 +02:00
> { { t ( "Manually invite new members" ) } } < br / >
2020-11-06 11:34:32 +01:00
< small > { {
2022-08-26 16:08:58 +02:00
t (
2020-11-06 11:34:32 +01:00
"The only way for your group to get new members is if an admininistrator invites them."
)
} } < / small >
2022-07-12 10:55:28 +02:00
< / o-radio >
2020-11-06 11:34:32 +01:00
< / div >
2022-07-12 10:55:28 +02:00
< o-field
2022-08-26 16:08:58 +02:00
: label = "t('Followers')"
: message = "t('Followers will receive new public events and posts.')"
2021-01-20 18:16:44 +01:00
>
2022-07-12 10:55:28 +02:00
< o-checkbox v-model = "editableGroup.manuallyApprovesFollowers" >
2022-08-26 16:08:58 +02:00
{ { t ( "Manually approve new followers" ) } }
2022-07-12 10:55:28 +02:00
< / o-checkbox >
< / o-field >
2021-01-20 18:16:44 +01:00
2020-08-05 16:44:08 +02:00
< full-address-auto-complete
2022-08-26 16:08:58 +02:00
: label = "t('Group address')"
2021-11-06 10:08:20 +01:00
v - model = "currentAddress"
: hideMap = "true"
2020-08-05 16:44:08 +02:00
/ >
2022-07-12 10:55:28 +02:00
< div class = "flex flex-wrap gap-2 my-2" >
< o-button native -type = " submit " variant = "primary" > { {
2022-08-26 16:08:58 +02:00
t ( "Update group" )
2022-07-12 10:55:28 +02:00
} } < / o-button >
< o-button @click ="confirmDeleteGroup" variant = "danger" > { {
2022-08-26 16:08:58 +02:00
t ( "Delete group" )
2022-07-12 10:55:28 +02:00
} } < / o-button >
2020-08-27 11:53:24 +02:00
< / div >
2020-07-09 17:24:28 +02:00
< / form >
2022-07-12 10:55:28 +02:00
< o-notification
variant = "danger"
v - for = "(value, index) in errors"
: key = "index"
>
2021-04-12 10:43:04 +02:00
{ { value } }
2022-07-12 10:55:28 +02:00
< / o-notification >
2020-07-09 17:24:28 +02:00
< / section >
2022-07-12 10:55:28 +02:00
< o-notification v -else -if = " ! loading " >
2022-08-26 16:08:58 +02:00
{ { t ( "You are not an administrator for this group." ) } }
2022-07-12 10:55:28 +02:00
< / o-notification >
2020-07-09 17:24:28 +02:00
< / div >
< / template >
2022-07-12 10:55:28 +02:00
< script lang = "ts" setup >
2020-09-29 09:53:48 +02:00
import PictureUpload from "@/components/PictureUpload.vue" ;
2022-07-12 10:55:28 +02:00
import { GroupVisibility , MemberRole , Openness } from "@/types/enums" ;
2022-08-26 16:08:58 +02:00
import { IGroup , usernameWithDomain , displayName } from "@/types/actor" ;
2022-07-12 10:55:28 +02:00
import { Address , IAddress } from "@/types/address.model" ;
2021-05-12 18:10:07 +02:00
import { ServerParseError } from "@apollo/client/link/http" ;
import { ErrorResponse } from "@apollo/client/link/error" ;
2021-06-15 17:25:33 +02:00
import RouteName from "@/router/name" ;
2021-06-16 11:25:53 +02:00
import { buildFileFromIMedia } from "@/utils/image" ;
2022-07-12 10:55:28 +02:00
import { useAvatarMaxSize , useBannerMaxSize } from "@/composition/config" ;
import { useI18n } from "vue-i18n" ;
import { computed , ref , watch , defineAsyncComponent , inject } from "vue" ;
import { useGroup , useUpdateGroup } from "@/composition/apollo/group" ;
import {
useCurrentActorClient ,
usePersonStatusGroup ,
} from "@/composition/apollo/actor" ;
import { DELETE _GROUP } from "@/graphql/group" ;
import { useMutation } from "@vue/apollo-composable" ;
import { useRouter } from "vue-router" ;
import { Dialog } from "@/plugins/dialog" ;
import { useHead } from "@vueuse/head" ;
import { Notifier } from "@/plugins/notifier" ;
2020-07-09 17:24:28 +02:00
2022-08-26 16:08:58 +02:00
const Editor = defineAsyncComponent (
( ) => import ( "@/components/TextEditor.vue" )
) ;
2020-07-09 17:24:28 +02:00
2022-10-05 12:13:19 +02:00
const FullAddressAutoComplete = defineAsyncComponent (
( ) => import ( "@/components/Event/FullAddressAutoComplete.vue" )
) ;
2022-07-12 10:55:28 +02:00
const props = defineProps < { preferredUsername : string } > ( ) ;
2021-04-12 10:43:04 +02:00
2022-07-12 10:55:28 +02:00
const { currentActor } = useCurrentActorClient ( ) ;
2021-04-12 10:43:04 +02:00
2022-08-26 16:08:58 +02:00
const {
group ,
loading ,
onResult : onGroupResult ,
} = useGroup ( props . preferredUsername ) ;
2020-09-29 09:53:48 +02:00
2022-07-12 10:55:28 +02:00
const { t } = useI18n ( { useScope : "global" } ) ;
2020-09-29 09:53:48 +02:00
2022-07-12 10:55:28 +02:00
useHead ( {
title : computed ( ( ) => t ( "Group settings" ) ) ,
} ) ;
2020-07-09 17:24:28 +02:00
2022-07-12 10:55:28 +02:00
const notifier = inject < Notifier > ( "notifier" ) ;
2022-01-10 15:19:16 +01:00
2022-07-12 10:55:28 +02:00
const avatarFile = ref < File | null > ( null ) ;
const bannerFile = ref < File | null > ( null ) ;
2020-08-05 16:44:08 +02:00
2022-07-12 10:55:28 +02:00
const errors = ref < string [ ] > ( [ ] ) ;
2020-11-06 11:34:32 +01:00
2022-07-12 10:55:28 +02:00
const showCopiedTooltip = ref ( false ) ;
2020-08-05 16:44:08 +02:00
2022-08-26 16:08:58 +02:00
const editableGroup = ref < IGroup > ( ) ;
2021-06-15 17:25:33 +02:00
2022-08-26 16:08:58 +02:00
const { onDone , onError , mutate : updateGroup } = useUpdateGroup ( ) ;
2022-07-12 10:55:28 +02:00
2022-08-26 16:08:58 +02:00
onDone ( ( ) => {
notifier ? . success ( t ( "Group settings saved" ) ) ;
} ) ;
2020-08-05 16:44:08 +02:00
2022-08-26 16:08:58 +02:00
onError ( ( err ) => {
handleError ( err as unknown as ErrorResponse ) ;
} ) ;
2022-07-12 10:55:28 +02:00
const copyURL = async ( ) : Promise < void > => {
await window . navigator . clipboard . writeText ( group . value ? . url ? ? "" ) ;
showCopiedTooltip . value = true ;
setTimeout ( ( ) => {
showCopiedTooltip . value = false ;
} , 2000 ) ;
} ;
2022-08-26 16:08:58 +02:00
onGroupResult ( ( { data } ) => {
editableGroup . value = data . group ;
} ) ;
watch (
group ,
2022-10-05 12:13:19 +02:00
async ( newGroup : IGroup | undefined , oldGroup : IGroup | undefined ) => {
2022-08-26 16:08:58 +02:00
console . debug ( "watching group" ) ;
if ( ! newGroup ) return ;
try {
if (
oldGroup ? . avatar !== undefined &&
oldGroup ? . avatar !== newGroup ? . avatar
) {
avatarFile . value = await buildFileFromIMedia ( newGroup ? . avatar ) ;
}
if (
oldGroup ? . banner !== undefined &&
oldGroup ? . banner !== newGroup ? . banner
) {
bannerFile . value = await buildFileFromIMedia ( newGroup ? . banner ) ;
}
} catch ( e ) {
// Catch errors while building media
console . error ( e ) ;
2021-06-16 11:25:53 +02:00
}
2022-08-26 16:08:58 +02:00
editableGroup . value = { ... newGroup } ;
} ,
{
immediate : true ,
2021-06-15 17:25:33 +02:00
}
2022-08-26 16:08:58 +02:00
) ;
2021-06-15 17:25:33 +02:00
2022-08-26 16:08:58 +02:00
const buildVariables = computed ( ( ) => {
2022-07-12 10:55:28 +02:00
let avatarObj = { } ;
let bannerObj = { } ;
const variables = { ... editableGroup . value } ;
let physicalAddress ;
if ( variables . physicalAddress ) {
physicalAddress = { ... variables . physicalAddress } ;
} else {
physicalAddress = variables . physicalAddress ;
}
2020-09-29 09:53:48 +02:00
2022-07-12 10:55:28 +02:00
// eslint-disable-next-line
// @ts-ignore
if ( variables . _ _typename ) {
2020-09-29 09:53:48 +02:00
// eslint-disable-next-line
// @ts-ignore
2022-07-12 10:55:28 +02:00
delete variables . _ _typename ;
}
// eslint-disable-next-line
// @ts-ignore
if ( physicalAddress && physicalAddress . _ _typename ) {
2021-11-06 10:08:20 +01:00
// eslint-disable-next-line
// @ts-ignore
2022-07-12 10:55:28 +02:00
delete physicalAddress . _ _typename ;
}
2022-10-05 18:17:11 +02:00
delete physicalAddress ? . pictureInfo ;
2022-07-12 10:55:28 +02:00
delete variables . avatar ;
delete variables . banner ;
2020-09-29 09:53:48 +02:00
2022-07-12 10:55:28 +02:00
if ( avatarFile . value ) {
avatarObj = {
avatar : {
media : {
name : avatarFile . value ? . name ,
alt : ` ${ editableGroup . value ? . preferredUsername } 's avatar ` ,
2022-08-26 16:08:58 +02:00
file : avatarFile . value ,
2020-09-29 09:53:48 +02:00
} ,
2022-07-12 10:55:28 +02:00
} ,
2020-09-29 09:53:48 +02:00
} ;
}
2022-07-12 10:55:28 +02:00
if ( bannerFile . value ) {
bannerObj = {
banner : {
media : {
name : bannerFile . value ? . name ,
alt : ` ${ editableGroup . value ? . preferredUsername } 's banner ` ,
2022-08-26 16:08:58 +02:00
file : bannerFile . value ,
2022-07-12 10:55:28 +02:00
} ,
} ,
} ;
2020-08-05 16:44:08 +02:00
}
2022-07-12 10:55:28 +02:00
return {
2022-08-26 16:08:58 +02:00
id : group . value ? . id ? ? "" ,
2022-07-12 10:55:28 +02:00
name : editableGroup . value ? . name ,
summary : editableGroup . value ? . summary ,
visibility : editableGroup . value ? . visibility ,
openness : editableGroup . value ? . openness ,
manuallyApprovesFollowers : editableGroup . value ? . manuallyApprovesFollowers ,
physicalAddress ,
... avatarObj ,
... bannerObj ,
} ;
2022-08-26 16:08:58 +02:00
} ) ;
2020-08-05 16:44:08 +02:00
2022-07-12 10:55:28 +02:00
const canShowCopyButton = computed ( ( ) : boolean => {
return window . isSecureContext ;
} ) ;
2021-04-12 10:43:04 +02:00
2022-07-12 10:55:28 +02:00
const currentAddress = computed ( {
get ( ) : IAddress {
return new Address ( editableGroup . value ? . physicalAddress ) ;
} ,
set ( address : IAddress ) {
2022-08-26 16:08:58 +02:00
if ( editableGroup . value ) {
editableGroup . value . physicalAddress = address ;
}
2022-07-12 10:55:28 +02:00
} ,
} ) ;
2021-11-06 10:08:20 +01:00
2022-07-12 10:55:28 +02:00
const avatarMaxSize = useAvatarMaxSize ( ) ;
const bannerMaxSize = useBannerMaxSize ( ) ;
2021-04-12 10:43:04 +02:00
2022-07-12 10:55:28 +02:00
const handleError = ( err : ErrorResponse ) => {
if ( err ? . networkError ? . name === "ServerParseError" ) {
const error = err ? . networkError as ServerParseError ;
2021-04-12 10:43:04 +02:00
2022-07-12 10:55:28 +02:00
if ( error ? . response ? . status === 413 ) {
errors . value . push (
t (
"Unable to create the group. One of the pictures may be too heavy."
) as string
) ;
2021-04-12 10:43:04 +02:00
}
}
2022-07-12 10:55:28 +02:00
errors . value . push (
... ( err . graphQLErrors || [ ] ) . map (
( { message } : { message : string } ) => message
)
) ;
} ;
const isCurrentActorAGroupAdmin = computed ( ( ) : boolean => {
return hasCurrentActorThisRole ( MemberRole . ADMINISTRATOR ) ;
} ) ;
const hasCurrentActorThisRole = ( givenRole : string | string [ ] ) : boolean => {
const roles = Array . isArray ( givenRole ) ? givenRole : [ givenRole ] ;
return (
personMemberships . value ? . total > 0 &&
roles . includes ( personMemberships . value ? . elements [ 0 ] . role )
) ;
} ;
const personMemberships = computed (
( ) => person . value ? . memberships ? ? { total : 0 , elements : [ ] }
) ;
const { person } = usePersonStatusGroup ( props . preferredUsername ) ;
const dialog = inject < Dialog > ( "dialog" ) ;
const confirmDeleteGroup = ( ) : void => {
console . debug ( "confirm delete group" , dialog ) ;
dialog ? . confirm ( {
title : t ( "Delete group" ) ,
message : t (
"Are you sure you want to <b>completely delete</b> this group? All members - including remote ones - will be notified and removed from the group, and <b>all of the group data (events, posts, discussions, todos…) will be irretrievably destroyed</b>."
) ,
confirmText : t ( "Delete group" ) ,
cancelText : t ( "Cancel" ) ,
2022-07-12 10:55:28 +02:00
variant : "danger" ,
2022-07-12 10:55:28 +02:00
hasIcon : true ,
onConfirm : ( ) =>
deleteGroupMutation ( {
groupId : group . value ? . id ,
} ) ,
} ) ;
} ;
const { mutate : deleteGroupMutation , onDone : deleteGroupDone } = useMutation < {
deleteGroup : IGroup ;
} > ( DELETE _GROUP ) ;
const router = useRouter ( ) ;
deleteGroupDone ( ( ) => {
router . push ( { name : RouteName . MY _GROUPS } ) ;
} ) ;
2020-07-09 17:24:28 +02:00
< / script >