From 9daea8494372f81e6ec4b5cd91034ace46a85616 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Janis=20Daniel=20Da=CC=88hne?=
 <janis.daehne2@student.uni-halle.de>
Date: Wed, 5 Jun 2019 22:29:12 +0200
Subject: [PATCH] - get file preview (asset) is working properly (only files
 accessible are loaded - some small asset dialog improvements

---
 i18n/en.ts                                    |   2 +-
 i18n/i18nRoot.ts                              |   2 +-
 .../changeCustomTestView.tsx                  |   8 +-
 .../changeTestDialog/changeTestView.tsx       |   9 +-
 .../editCustomProjectSite.tsx                 |   6 +-
 .../addAssetFilesModal/addAssetFilesModal.tsx | 152 ++++++++++--------
 .../assetsPanel/assetsPanelView.tsx           |   5 +-
 .../exerciseEditorSite/exerciseEditorSite.tsx |   7 +-
 .../dialogs/changeTestView.tsx                |   7 +-
 .../dialogs/testFilesListView.tsx             |  76 ++++-----
 .../singleGroupSite/headerBarContent.tsx      |  34 ++--
 .../sites/singleGroupSite/singleGroupSite.tsx |   9 +-
 src/constants.ts                              |   2 +-
 13 files changed, 167 insertions(+), 152 deletions(-)

diff --git a/i18n/en.ts b/i18n/en.ts
index 3ad9d299..77346568 100644
--- a/i18n/en.ts
+++ b/i18n/en.ts
@@ -898,7 +898,7 @@ export const lang_en: LangObj = {
   "Manage asset files" : "Manage asset files",
   "Attached Files" : "Attached Files",
   "All Files": "All Files",
-  "These are all files where you have access to" : "These are all files where you have access to",
+  "These are all files where you have access to. For exercises or exercise tests you need permission to change the exercise." : "These are all files where you have access to. For exercises or exercise tests you need permission to change the exercise.",
   "Note that when you detach a file and it has no connection to another exercise, it will get deleted!" : "Note that when you detach a file and it has no connection to another exercise then it will get deleted!",
   "Managing assets is only allowed for saved exercises (not for new exercises)": "Managing assets is only allowed for saved exercises (not for new exercises)",
   "All changes to assets are applied immediately in the backend (saving is not needed for this)!": "All changes to assets are applied immediately in the backend (saving is not needed for this)!",
diff --git a/i18n/i18nRoot.ts b/i18n/i18nRoot.ts
index e28d91ec..da3b9ba3 100644
--- a/i18n/i18nRoot.ts
+++ b/i18n/i18nRoot.ts
@@ -905,7 +905,7 @@ export interface LangObj {
   "Manage asset files": string
   "Attached Files" : string
   "All Files": string
-  "These are all files where you have access to" : string
+  "These are all files where you have access to. For exercises or exercise tests you need permission to change the exercise." : string
   "Note that when you detach a file and it has no connection to another exercise, it will get deleted!": string
   "Managing assets is only allowed for saved exercises (not for new exercises)": string
   "All changes to assets are applied immediately in the backend (saving is not needed for this)!": string
diff --git a/src/components/sites/doExerciseSite/customTestsPanel/dialog/customTestsDialog/changeCustomTestView.tsx b/src/components/sites/doExerciseSite/customTestsPanel/dialog/customTestsDialog/changeCustomTestView.tsx
index 686aeed1..7260c97c 100644
--- a/src/components/sites/doExerciseSite/customTestsPanel/dialog/customTestsDialog/changeCustomTestView.tsx
+++ b/src/components/sites/doExerciseSite/customTestsPanel/dialog/customTestsDialog/changeCustomTestView.tsx
@@ -247,11 +247,6 @@ class ChangeCustomTestView extends React.Component<Props, any> {
           uploadPercentage={this.props.addCustomTestAssetFilesModalUploadPercentage}
           uploadInputId={'do-exercise-custom-test-upload'}
 
-
-          onLoadAllAssetFilePreviews={async () => {
-            await this.props.getPossibleAllUserFilesAsync()
-          }}
-
           onUploadFile={async (file) => {
 
             this.props.setIsAddCustomTestAssetFilesModalDisplayed(true, true)
@@ -520,7 +515,8 @@ class ChangeCustomTestView extends React.Component<Props, any> {
 
                             if (this.props.test.id <= 0) return
 
-                            this.props.setIsAddCustomTestAssetFilesModalDisplayed(true, false)
+                            this.props.setIsAddCustomTestAssetFilesModalDisplayed(true, false) //before loading because this will cancel isBusy
+                            this.props.getPossibleAllUserFilesAsync()
                           }}
                      >
                        <Icon name="write"/>
diff --git a/src/components/sites/editCustomProjectSite/dialogs/changeTestDialog/changeTestView.tsx b/src/components/sites/editCustomProjectSite/dialogs/changeTestDialog/changeTestView.tsx
index 6b238945..af473aba 100644
--- a/src/components/sites/editCustomProjectSite/dialogs/changeTestDialog/changeTestView.tsx
+++ b/src/components/sites/editCustomProjectSite/dialogs/changeTestDialog/changeTestView.tsx
@@ -262,9 +262,6 @@ class ChangeTestView extends React.Component<Props, any> {
             title={getI18n(this.props.langId, "Manage asset files")}
             isDisplayed={this.props.isAddCustomProjectCustomTestAssetFilesModalDisplayed}
             isBusy={this.props.isAddCustomProjectCustomTestAssetFilesModalBusy}
-            onLoadAllAssetFilePreviews={async () => {
-              await this.props.getPossibleAllUserFilesAsync()
-            }}
             uploadPercentage={this.props.addCustomProjectCustomTestAssetFilesModalUploadPercentage}
             uploadInputId={'custom-project-custom-test-upload'}
             onUploadFile={async (file) => {
@@ -596,7 +593,11 @@ class ChangeTestView extends React.Component<Props, any> {
                                            ? 'clickable'
                                            : 'div-disabled'} style={{marginBottom: '0.5em'}}
                                 onClick={() => {
-                                  this.props.setIsAddCustomProjectCustomTestAssetFilesModalDisplayed(true, false)
+
+                                  if (this.props.test.id <= 0) return
+
+                                  this.props.setIsAddCustomProjectCustomTestAssetFilesModalDisplayed(true, false) //before loading because this will cancel isBusy
+                                  this.props.getPossibleAllUserFilesAsync()
                                 }}
                            >
                              <Icon name="write"/>
diff --git a/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx b/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx
index 9340b586..3f083fce 100644
--- a/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx
+++ b/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx
@@ -317,9 +317,6 @@ class editCustomProjectSite extends React.Component<Props & RouteComponentProps<
           title={getI18n(this.props.langId, "Manage asset files")}
           isDisplayed={this.props.isAddCustomProjectAssetFilesModalDisplayed}
           isBusy={this.props.isAddCustomProjectAssetFilesModalBusy}
-          onLoadAllAssetFilePreviews={async () => {
-            await this.props.getPossibleAllUserFilesAsync()
-          }}
           uploadPercentage={this.props.addCustomProjectAssetFilesModalUploadPercentage}
           uploadInputId={'custom-project-description-upload'}
 
@@ -651,7 +648,8 @@ class editCustomProjectSite extends React.Component<Props & RouteComponentProps<
                         this.props.setCustomProjectDescriptionAssets(assets)
                       }}
                       openAddAssetsModal={() => {
-                        this.props.setIsAddCustomProjectAssetFilesModalDisplayed(true, false)
+                        this.props.setIsAddCustomProjectAssetFilesModalDisplayed(true, false) //before loading because this will cancel isBusy
+                        this.props.getPossibleAllUserFilesAsync()
                       }}
                     />
                   }
diff --git a/src/components/sites/exerciseEditorSite/addAssetFilesModal/addAssetFilesModal.tsx b/src/components/sites/exerciseEditorSite/addAssetFilesModal/addAssetFilesModal.tsx
index 672fd81a..a01739ce 100644
--- a/src/components/sites/exerciseEditorSite/addAssetFilesModal/addAssetFilesModal.tsx
+++ b/src/components/sites/exerciseEditorSite/addAssetFilesModal/addAssetFilesModal.tsx
@@ -20,6 +20,7 @@ import Logger from '../../../../helpers/logger'
 import {askDialog, errorDialog} from '../../../../helpers/dialogHelper'
 import {MyPopup} from '../../../helpers/myPopup'
 import {leftIconPopupOffset} from '../../../../constants'
+import orderBy from 'lodash-es/orderBy'
 
 //const css = require('./styles.styl');
 
@@ -105,7 +106,6 @@ export interface MyProps {
    */
   readonly onClose: () => void
 
-  readonly onLoadAllAssetFilePreviews: () => Promise<void>
 }
 
 const mapStateToProps = (rootState: RootState, props: MyProps) => {
@@ -127,12 +127,7 @@ const stateProps = returntypeof(mapStateToProps);
 const dispatchProps = returntypeof(mapDispatchToProps);
 type Props = typeof stateProps & typeof dispatchProps;
 
-class changeCodeTemplateDialog extends React.Component<Props, any> {
-
-
-  async componentWillMount(): Promise<void> {
-    await this.props.onLoadAllAssetFilePreviews()
-  }
+class addAssetFilesModal extends React.Component<Props, any> {
 
   async onUploadFile(): Promise<void> {
 
@@ -184,7 +179,10 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
               {
                 //list with asset files that can be added
               }
-              <div className="flexed-h fh" style={{maxWidth: '450px', minWidth: '300px'}}>
+              <div className="flexed-h fh" style={{
+                maxWidth: '450px',
+                minWidth: '300px'
+              }}>
 
                 <div className="flexed" style={{
                   justifyContent: 'space-between',
@@ -192,9 +190,13 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
                 }}>
 
                   <div className="flexed-header-with-tooltip">
-                    <h3 className="mar-right-half">{getI18n(this.props.langId, "All Files")}</h3>
+                    <h3 className="mar-right-half">{getI18n(this.props.langId,
+                                                            "All Files"
+                    )} ({this.props.allAssetFilePreviews.length})</h3>
                     <HelpPopup
-                      defaultText={getI18n(this.props.langId, "These are all files where you have access to")}/>
+                      defaultText={getI18n(this.props.langId,
+                                           "These are all files where you have access to. For exercises or exercise tests you need permission to change the exercise."
+                      )}/>
                   </div>
 
                   <div className="clickable" onClick={() => {
@@ -236,11 +238,16 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
                   <List>
                     {
                       //filter out all that are already attached
-                      this.props.allAssetFilePreviews
-                        .filter(p => p.originalName.toLowerCase().indexOf(this.props.filterText.toLowerCase()) !== -1)
+                      orderBy(
+                        this.props.allAssetFilePreviews
+                          .filter(
+                            p => p.originalName.toLowerCase().indexOf(this.props.filterText.toLowerCase()) !== -1),
+                        p => p.originalName.toLowerCase()
+                      )
                         .map((assetFilePreview: FilePreviewFromBackend) => {
 
-                          const alreadyAdded = this.props.attachedAssetFilePreviews.some(p => assetFilePreview.id === p.id)
+                          const alreadyAdded = this.props.attachedAssetFilePreviews.some(
+                            p => assetFilePreview.id === p.id)
 
                           return (
                             <List.Item key={assetFilePreview.id} className={alreadyAdded
@@ -257,7 +264,7 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
                               <List.Content>
                                 <List.Header>
                                   <div className="flexed">
-                                    <div className="mar-right-half">
+                                    <div className="mar-right-half brake-words">
                                       {
                                         assetFilePreview.originalName
                                       }
@@ -286,10 +293,12 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
               {
                 //current asset files attached to the exercise
               }
-              <div>
+              <div style={{flex: '1'}}>
 
                 <div className="flexed-header-with-tooltip">
-                  <h3 className="mar-right-half">{getI18n(this.props.langId, "Attached Files")}</h3>
+                  <h3 className="mar-right-half">{getI18n(this.props.langId,
+                                                          "Attached Files"
+                  )} ({this.props.attachedAssetFilePreviews.length})</h3>
                   <HelpPopup defaultText={getI18n(this.props.langId,
                                                   "Note that when you detach a file and it has no connection to another exercise, it will get deleted!"
                   )}/>
@@ -297,73 +306,76 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
 
                 <List>
                   {
-                    this.props.attachedAssetFilePreviews.map((assetFilePreview: FilePreviewFromBackend) => {
+                    orderBy(this.props.attachedAssetFilePreviews, p => p.originalName.toLowerCase())
+                      .map((assetFilePreview: FilePreviewFromBackend) => {
 
-                      const cannotBeDetached = this.props.notDetachableAssetFilePreviews !== undefined && this.props.notDetachableAssetFilePreviews.some(p => p.id === assetFilePreview.id)
+                        const cannotBeDetached = this.props.notDetachableAssetFilePreviews !== undefined && this.props.notDetachableAssetFilePreviews.some(
+                          p => p.id === assetFilePreview.id)
 
-                      return (
-                        <List.Item key={assetFilePreview.id}>
+                        return (
+                          <List.Item key={assetFilePreview.id}>
 
-                          {
-                            cannotBeDetached &&
+                            {
+                              cannotBeDetached &&
                               <MyPopup
                                 trigger={
-                                  <List.Icon className="hoverable" name="question circle" size='large' verticalAlign='middle'/>
+                                  <List.Icon className="hoverable" name="question circle" size='large'
+                                             verticalAlign='middle'/>
                                 }
                                 on="hover"
                                 horizontalOffset={leftIconPopupOffset}
                                 content={this.props.notDetachableReason}
                               />
 
-                          }
-                          {
-                            !cannotBeDetached &&
-                            <List.Icon className="clickable" name='trash' size='large' verticalAlign='middle'
-                                       onClick={async () => {
-
-                                         const should = await askDialog(getI18n(this.props.langId, "Detach file"),
-                                                                        getI18n(this.props.langId,
-                                                                                "Are you sure you want to detach the file? If this file is not connected to another exercise the fille will get deleted!"
-                                                                        ), this.props.langId
-                                         )
-
-                                         if (!should) return
-
-                                         this.props.onDetachAssetFilePreview(assetFilePreview)
-                                       }}
-                            />
-                          }
-
-
-                          <List.Content>
-                            <List.Header>
-                              <div className="flexed">
-                                <div className="mar-right-half">
-                                  {
-                                    assetFilePreview.originalName
-                                  }
-                                </div>
+                            }
+                            {
+                              !cannotBeDetached &&
+                              <List.Icon className="clickable" name='trash' size='large' verticalAlign='middle'
+                                         onClick={async () => {
+
+                                           const should = await askDialog(getI18n(this.props.langId, "Detach file"),
+                                                                          getI18n(this.props.langId,
+                                                                                  "Are you sure you want to detach the file? If this file is not connected to another exercise the fille will get deleted!"
+                                                                          ), this.props.langId
+                                           )
+
+                                           if (!should) return
+
+                                           this.props.onDetachAssetFilePreview(assetFilePreview)
+                                         }}
+                              />
+                            }
+
+
+                            <List.Content>
+                              <List.Header>
+                                <div className="flexed">
+                                  <div className="mar-right-half brake-words">
+                                    {
+                                      assetFilePreview.originalName
+                                    }
+                                  </div>
 
-                                <div className="not-important-colored">
-                                  (
-                                  {convertSizeToHumanReadableSize(assetFilePreview.sizeInBytes)}
-                                  )
+                                  <div className="not-important-colored">
+                                    (
+                                    {convertSizeToHumanReadableSize(assetFilePreview.sizeInBytes)}
+                                    )
+                                  </div>
                                 </div>
-                              </div>
-                            </List.Header>
-                            <List.Description>
-                              <div className="flexed">
-                                <div className="mar-right-half not-important-colored">
-                                  {
-                                    assetFilePreview.mimeType
-                                  }
+                              </List.Header>
+                              <List.Description>
+                                <div className="flexed">
+                                  <div className="mar-right-half not-important-colored">
+                                    {
+                                      assetFilePreview.mimeType
+                                    }
+                                  </div>
                                 </div>
-                              </div>
-                            </List.Description>
-                          </List.Content>
-                        </List.Item>
-                      )
-                    })
+                              </List.Description>
+                            </List.Content>
+                          </List.Item>
+                        )
+                      })
                   }
                 </List>
 
@@ -394,4 +406,4 @@ class changeCodeTemplateDialog extends React.Component<Props, any> {
   }
 }
 
-export default connect(mapStateToProps, mapDispatchToProps)(changeCodeTemplateDialog)
+export default connect(mapStateToProps, mapDispatchToProps)(addAssetFilesModal)
diff --git a/src/components/sites/exerciseEditorSite/assetsPanel/assetsPanelView.tsx b/src/components/sites/exerciseEditorSite/assetsPanel/assetsPanelView.tsx
index d21a0691..7b52385f 100644
--- a/src/components/sites/exerciseEditorSite/assetsPanel/assetsPanelView.tsx
+++ b/src/components/sites/exerciseEditorSite/assetsPanel/assetsPanelView.tsx
@@ -15,6 +15,8 @@ import {setExerciseEditorAssets} from "../../../../state/actions/exerciseEditorS
 import {getCopyAssetUrlText} from "../../../../helpers/assetHelper";
 import {assetPrefix} from '../../../../constants'
 import Logger from '../../../../helpers/logger'
+import orderBy from 'lodash-es/orderBy'
+
 const fileSaver = require('file-saver');
 
 //const css = require('./styles.styl');
@@ -49,7 +51,8 @@ class AssetsPanelView extends React.Component<Props, any> {
 
         <List>
           {
-            this.props.assets.map((asset: FileWithData) => {
+            orderBy(this.props.assets, p => p.displayName)
+              .map((asset: FileWithData) => {
               return (
                 <List.Item key={asset.id}>
                   <List.Icon size='large' verticalAlign='middle'>
diff --git a/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx b/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx
index 295c146d..3cb02ac3 100644
--- a/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx
+++ b/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx
@@ -289,10 +289,6 @@ class exerciseEditorSite extends React.Component<Props & RouteComponentProps<Mat
             isBusy={this.props.isAddAssetFilesModalBusy}
             uploadInputId={'exercise-description-upload'}
 
-            onLoadAllAssetFilePreviews={async () => {
-              this.props.getMarkdownAssetPreviewsAsync(this.props.editorExercise.id)
-            }}
-
             onUploadFile={async (file) => {
 
               this.props.setIsAddAssetFilesModalDisplayed(true, true)
@@ -545,7 +541,8 @@ class exerciseEditorSite extends React.Component<Props & RouteComponentProps<Mat
                         this.props.setExerciseEditorAssets(assets)
                       }}
                       openAddAssetsModal={() => {
-                        this.props.setIsAddAssetFilesModalDisplayed(true, false)
+                        this.props.setIsAddAssetFilesModalDisplayed(true, false) //before loading because this will cancel isBusy
+                        this.props.getMarkdownAssetPreviewsAsync(this.props.editorExercise.id)
                       }}
                     />
                   }
diff --git a/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/changeTestView.tsx b/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/changeTestView.tsx
index fbd9b9d9..aa212aa5 100644
--- a/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/changeTestView.tsx
+++ b/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/changeTestView.tsx
@@ -256,10 +256,6 @@ class ChangeTestView extends React.Component<Props, any> {
             uploadInputId={'exercise-test-upload'}
             uploadPercentage={this.props.addTestAssetFilesModalUploadPercentage}
 
-            onLoadAllAssetFilePreviews={async () => {
-              await this.props.getTestAssetPreviewsAsync(this.props.exerciseId)
-            }}
-
             onUploadFile={async (file) => {
 
               this.props.setIsAddTestAssetFilesModalDisplayed(true, true)
@@ -595,7 +591,8 @@ class ChangeTestView extends React.Component<Props, any> {
 
                              if (this.props.test.id <= 0) return
 
-                             this.props.setIsAddTestAssetFilesModalDisplayed(true, false)
+                             this.props.setIsAddTestAssetFilesModalDisplayed(true, false) //before loading because this will cancel isBusy
+                             this.props.getTestAssetPreviewsAsync(this.props.exerciseId)
                            }}
                       >
                         <Icon name="write"/>
diff --git a/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/testFilesListView.tsx b/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/testFilesListView.tsx
index ee1f7f03..7bd5526d 100644
--- a/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/testFilesListView.tsx
+++ b/src/components/sites/exerciseEditorSite/exerciseTestsPanel/dialogs/testFilesListView.tsx
@@ -12,6 +12,7 @@ import {Icon, List} from "semantic-ui-react";
 import {convertSizeToHumanReadableSize} from "../../../../../helpers/convertersAndTransformers";
 import {copyToClipboard} from '../../../../../helpers/clipboard'
 import {getCopyAssetUrlText} from '../../../../../helpers/assetHelper'
+import orderBy from 'lodash-es/orderBy'
 
 //const css = require('./styles.styl');
 
@@ -54,11 +55,10 @@ class changeTestFilesListView extends React.Component<Props, any> {
       <div>
         <List>
           {
-            this.props.files.map((file: FilePreviewFromBackend) => {
-
-
-              return (
-                <List.Item key={file.id}>
+            orderBy(this.props.files, p => p.originalName)
+              .map((file: FilePreviewFromBackend) => {
+                return (
+                  <List.Item key={file.id}>
                     <List.Icon size='large' verticalAlign='middle'>
 
                       <a target="_blank" download
@@ -66,50 +66,50 @@ class changeTestFilesListView extends React.Component<Props, any> {
                         <Icon name="download"/>
                       </a>
 
-                  </List.Icon>
-                  <List.Content>
-                    <List.Header>
+                    </List.Icon>
+                    <List.Content>
+                      <List.Header>
 
-                      <div className="flexed">
-                        <div className="mar-right-half">
+                        <div className="flexed">
+                          <div className="mar-right-half">
 
                           <span>
                             {
                               file.originalName
                             }
                           </span>
+                          </div>
+
+                          <div className="clickable"
+                               onClick={() => {
+                                 copyToClipboard(file.originalName)
+                               }}
+                          >
+                            <Icon name="copy"/>
+                          </div>
+
+                          <div className="not-important-colored">
+                            (
+                            {convertSizeToHumanReadableSize(file.sizeInBytes)}
+                            )
+                          </div>
                         </div>
 
-                        <div className="clickable"
-                             onClick={() => {
-                               copyToClipboard(file.originalName)
-                             }}
-                        >
-                          <Icon name="copy"/>
-                        </div>
+                      </List.Header>
+                      <List.Description>
+                        <div className="flexed">
+                          <div className="not-important-colored mar-right-half">
+                            {
+                              file.mimeType
+                            }
+                          </div>
 
-                        <div className="not-important-colored">
-                          (
-                          {convertSizeToHumanReadableSize(file.sizeInBytes)}
-                          )
-                        </div>
-                      </div>
-
-                    </List.Header>
-                    <List.Description>
-                      <div className="flexed">
-                        <div className="not-important-colored mar-right-half">
-                          {
-                            file.mimeType
-                          }
                         </div>
-
-                      </div>
-                    </List.Description>
-                  </List.Content>
-                </List.Item>
-              )
-            })
+                      </List.Description>
+                    </List.Content>
+                  </List.Item>
+                )
+              })
           }
         </List>
       </div>
diff --git a/src/components/sites/singleGroupSite/headerBarContent.tsx b/src/components/sites/singleGroupSite/headerBarContent.tsx
index 556be998..991a4ec5 100644
--- a/src/components/sites/singleGroupSite/headerBarContent.tsx
+++ b/src/components/sites/singleGroupSite/headerBarContent.tsx
@@ -28,7 +28,8 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
   return {
     //test0: rootState...
     //test: props.test
-    isLoading: rootState.singleGroupState.isLoading,
+    isLoading: rootState.singleGroupState.isLoading
+      || rootState.globalSystemSettingsState.isLoading,
     groupId: rootState.singleGroupState.selectedGroupId,
     defaultUserGroupId: rootState.globalSystemSettingsState.systemSettings.defaultUserGroupId,
     group: rootState.singleGroupState.group,
@@ -39,10 +40,10 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
 }
 
 const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({
-  //imported reducer funcs here
-  setIsAddUsersDialogDisplayed,
-  removeMultipleMemberAndRefreshAsync
-}, dispatch)
+                                                                        //imported reducer funcs here
+                                                                        setIsAddUsersDialogDisplayed,
+                                                                        removeMultipleMemberAndRefreshAsync
+                                                                      }, dispatch)
 
 
 const stateProps = returntypeof(mapStateToProps);
@@ -54,7 +55,9 @@ class headerBarContent extends React.Component<Props, any> {
 
     const isSomeUserSelected = this.props.members.some(p => p.uiIsSelected)
 
-    const currentUserGroupRole = this.props.group === null ? null : this.props.group.groupRole
+    const currentUserGroupRole = this.props.group === null
+                                 ? null
+                                 : this.props.group.groupRole
 
     const isDefaultUserGroup = this.props.groupId === this.props.defaultUserGroupId
 
@@ -69,7 +72,7 @@ class headerBarContent extends React.Component<Props, any> {
             <Icon name="plus"/>
             <span>
             {
-              getI18n(this.props.langId,'Add member')
+              getI18n(this.props.langId, 'Add member')
             }
           </span>
           </div>
@@ -81,8 +84,10 @@ class headerBarContent extends React.Component<Props, any> {
         }
 
         {
-          (currentUserGroupRole && currentUserGroupRole.canRemoveMemberFromGroup)&&
-          <div className={isSomeUserSelected && isDefaultUserGroup === false  ? 'clickable' : 'div-disabled'}
+          (currentUserGroupRole && currentUserGroupRole.canRemoveMemberFromGroup) &&
+          <div className={isSomeUserSelected && isDefaultUserGroup === false
+                          ? 'clickable'
+                          : 'div-disabled'}
                onClick={async () => {
 
                  if (isDefaultUserGroup) return
@@ -97,8 +102,10 @@ class headerBarContent extends React.Component<Props, any> {
 
                  if (userIds.length > 0) {
 
-                   shouldRelease = await askDialog(getI18n(this.props.langId,'Release multiple users from group'),
-                     `${getI18n(this.props.langId,'Are you sure you want to release multiple users from this group?')} (${userIds.length} users)`, this.props.langId
+                   shouldRelease = await askDialog(getI18n(this.props.langId, 'Release multiple users from group'),
+                                                   `${getI18n(this.props.langId,
+                                                              'Are you sure you want to release multiple users from this group?'
+                                                   )} (${userIds.length} users)`, this.props.langId
                    )
                  }
 
@@ -111,7 +118,7 @@ class headerBarContent extends React.Component<Props, any> {
             <Icon name="remove"/>
             <span>
             {
-              getI18n(this.props.langId,'Release selected member')
+              getI18n(this.props.langId, 'Release selected member')
             }
                </span>
           </div>
@@ -123,7 +130,8 @@ class headerBarContent extends React.Component<Props, any> {
 
 
     return (
-      <CommonHeaderBarContentWrapper leftArea={leftArea} disabled={this.props.isLoading || this.props.isSomeDialogDisplayed}/>
+      <CommonHeaderBarContentWrapper leftArea={leftArea}
+                                     disabled={this.props.isLoading || this.props.isSomeDialogDisplayed}/>
     )
   }
 }
diff --git a/src/components/sites/singleGroupSite/singleGroupSite.tsx b/src/components/sites/singleGroupSite/singleGroupSite.tsx
index a8058f56..22143346 100644
--- a/src/components/sites/singleGroupSite/singleGroupSite.tsx
+++ b/src/components/sites/singleGroupSite/singleGroupSite.tsx
@@ -42,7 +42,8 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
     //we cannot do this another way because there is no backend func to give us the group + members + roles...
     groups: rootState.singleGroupState.group,
     groupId: rootState.singleGroupState.selectedGroupId,
-    isLoading: rootState.singleGroupState.isLoading,
+    isLoading: rootState.singleGroupState.isLoading
+      || rootState.globalSystemSettingsState.isLoading,
     isAddUsersDialogDisplayed: rootState.singleGroupState.isAddUsersDialogDisplayed,
     groupRoles: rootState.singleGroupState.groupRoles,
     currentUserId: rootState.userDataSettingsSate.userData.id,
@@ -79,10 +80,12 @@ class SingleGroupSite extends React.Component<Props & RouteComponentProps<Matche
       if (groupId !== null) {
         await this.props.loadSingleGroupSite(groupId)
       } else {
-        errorDialog(getI18n(this.props.langId, "Error"), getI18n(this.props.langId, "Could not load site data"),this.props.langId)
+        errorDialog(getI18n(this.props.langId, "Error"), getI18n(this.props.langId, "Could not load site data"),
+                    this.props.langId
+        )
         Logger.error('Could not load site data because groupId was null')
       }
-    } catch(err) {
+    } catch (err) {
       ErrorHelper.makeServerErrorDialog(err, this.props.langId, 'Error', 'Could not load site data')
     }
 
diff --git a/src/constants.ts b/src/constants.ts
index 731637e0..e20e078c 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -13,7 +13,7 @@ import Logger from './helpers/logger'
  * y - breaking changes / new features
  * z - fixes, small changes
  */
-export const versionString = '2.4.7'
+export const versionString = '2.4.8'
 
 
 /**
-- 
GitLab