@sapui5/sap.fe.core 1.99.0 → 1.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/src/sap/fe/core/.library +1 -1
- package/src/sap/fe/core/AnnotationHelper.js +1 -69
- package/src/sap/fe/core/AnnotationHelper.ts +0 -70
- package/src/sap/fe/core/AppComponent.js +388 -399
- package/src/sap/fe/core/AppComponent.ts +403 -0
- package/src/sap/fe/core/AppStateHandler.js +127 -158
- package/src/sap/fe/core/BaseController.js +82 -58
- package/src/sap/fe/core/BaseController.ts +68 -0
- package/src/sap/fe/core/CommonUtils.js +111 -20
- package/src/sap/fe/core/CommonUtils.ts +131 -32
- package/src/sap/fe/core/ExtensionAPI.js +278 -274
- package/src/sap/fe/core/ExtensionAPI.ts +250 -0
- package/src/sap/fe/core/PageController.js +157 -29
- package/src/sap/fe/core/PageController.ts +60 -32
- package/src/sap/fe/core/RouterProxy.js +694 -756
- package/src/sap/fe/core/RouterProxy.ts +11 -9
- package/src/sap/fe/core/Synchronization.js +21 -31
- package/src/sap/fe/core/TemplateComponent.js +1 -1
- package/src/sap/fe/core/TemplateComponent.ts +10 -3
- package/src/sap/fe/core/TemplateModel.js +20 -38
- package/src/sap/fe/core/TemplateModel.ts +1 -1
- package/src/sap/fe/core/TransactionHelper.js +1354 -1370
- package/src/sap/fe/core/TransactionHelper.ts +33 -22
- package/src/sap/fe/core/actions/collaboration/ActivitySync.js +392 -0
- package/src/sap/fe/core/actions/collaboration/ActivitySync.ts +355 -0
- package/src/sap/fe/core/actions/collaboration/CollaborationCommon.js +136 -0
- package/src/sap/fe/core/actions/collaboration/CollaborationCommon.ts +119 -0
- package/src/sap/fe/core/actions/collaboration/Manage.js +262 -0
- package/src/sap/fe/core/actions/collaboration/Manage.ts +244 -0
- package/src/sap/fe/core/actions/collaboration/ManageDialog.fragment.xml +103 -0
- package/src/sap/fe/core/actions/collaboration/UserDetails.fragment.xml +13 -0
- package/src/sap/fe/core/actions/draft.js +30 -68
- package/src/sap/fe/core/actions/draft.ts +44 -71
- package/src/sap/fe/core/actions/messageHandling.js +55 -36
- package/src/sap/fe/core/actions/messageHandling.ts +66 -46
- package/src/sap/fe/core/actions/operations.js +34 -15
- package/src/sap/fe/core/actions/operations.ts +48 -18
- package/src/sap/fe/core/actions/sticky.js +17 -4
- package/src/sap/fe/core/actions/sticky.ts +21 -4
- package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +69 -66
- package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.ts +65 -0
- package/src/sap/fe/core/controllerextensions/EditFlow.js +1593 -1669
- package/src/sap/fe/core/controllerextensions/EditFlow.ts +1672 -0
- package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +79 -54
- package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.ts +61 -0
- package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +718 -779
- package/src/sap/fe/core/controllerextensions/InternalEditFlow.ts +783 -0
- package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +796 -816
- package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.ts +803 -0
- package/src/sap/fe/core/controllerextensions/InternalRouting.js +1004 -1005
- package/src/sap/fe/core/controllerextensions/InternalRouting.ts +978 -0
- package/src/sap/fe/core/controllerextensions/KPIManagement.js +487 -521
- package/src/sap/fe/core/controllerextensions/KPIManagement.ts +2 -2
- package/src/sap/fe/core/controllerextensions/MassEdit.js +141 -169
- package/src/sap/fe/core/controllerextensions/MassEdit.ts +156 -0
- package/src/sap/fe/core/controllerextensions/MessageHandler.js +233 -244
- package/src/sap/fe/core/controllerextensions/MessageHandler.ts +225 -0
- package/src/sap/fe/core/controllerextensions/PageReady.js +301 -336
- package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -10
- package/src/sap/fe/core/controllerextensions/Paginator.js +188 -175
- package/src/sap/fe/core/controllerextensions/Paginator.ts +163 -0
- package/src/sap/fe/core/controllerextensions/Placeholder.js +157 -149
- package/src/sap/fe/core/controllerextensions/Placeholder.ts +151 -0
- package/src/sap/fe/core/controllerextensions/Routing.js +144 -125
- package/src/sap/fe/core/controllerextensions/Routing.ts +132 -0
- package/src/sap/fe/core/controllerextensions/RoutingListener.js +7 -6
- package/src/sap/fe/core/controllerextensions/RoutingListener.ts +3 -0
- package/src/sap/fe/core/controllerextensions/Share.js +230 -268
- package/src/sap/fe/core/controllerextensions/Share.ts +231 -0
- package/src/sap/fe/core/controllerextensions/SideEffects.js +592 -633
- package/src/sap/fe/core/controllerextensions/SideEffects.ts +8 -7
- package/src/sap/fe/core/controllerextensions/ViewState.js +788 -806
- package/src/sap/fe/core/controllerextensions/ViewState.ts +805 -0
- package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +2 -2
- package/src/sap/fe/core/controls/ActionPartial.fragment.xml +2 -2
- package/src/sap/fe/core/controls/CommandExecution.js +67 -66
- package/src/sap/fe/core/controls/CommandExecution.ts +72 -0
- package/src/sap/fe/core/controls/ConditionalWrapper.js +90 -75
- package/src/sap/fe/core/controls/ConditionalWrapper.ts +83 -0
- package/src/sap/fe/core/controls/CustomQuickViewPage.js +130 -125
- package/src/sap/fe/core/controls/CustomQuickViewPage.ts +126 -0
- package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +103 -112
- package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.ts +101 -0
- package/src/sap/fe/core/controls/FieldWrapper.js +122 -135
- package/src/sap/fe/core/controls/FieldWrapper.ts +115 -0
- package/src/sap/fe/core/controls/FilterBar.js +162 -159
- package/src/sap/fe/core/controls/FilterBar.ts +143 -0
- package/src/sap/fe/core/controls/FormElementWrapper.js +45 -39
- package/src/sap/fe/core/controls/FormElementWrapper.ts +40 -0
- package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +37 -42
- package/src/sap/fe/core/controls/MultiValueParameterDelegate.ts +31 -0
- package/src/sap/fe/core/controls/filterbar/FilterContainer.js +126 -116
- package/src/sap/fe/core/controls/filterbar/FilterContainer.ts +98 -0
- package/src/sap/fe/core/controls/filterbar/VisualFilter.js +241 -255
- package/src/sap/fe/core/controls/filterbar/VisualFilter.ts +245 -0
- package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +150 -141
- package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.ts +125 -0
- package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +335 -322
- package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.ts +337 -0
- package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +74 -74
- package/src/sap/fe/core/controls/massEdit/MassEditHandlers.ts +70 -0
- package/src/sap/fe/core/converters/ConverterContext.js +348 -379
- package/src/sap/fe/core/converters/ConverterContext.ts +19 -16
- package/src/sap/fe/core/converters/ManifestSettings.js +12 -1
- package/src/sap/fe/core/converters/ManifestSettings.ts +12 -1
- package/src/sap/fe/core/converters/ManifestWrapper.js +354 -378
- package/src/sap/fe/core/converters/ManifestWrapper.ts +10 -0
- package/src/sap/fe/core/converters/MetaModelConverter.js +6 -4
- package/src/sap/fe/core/converters/MetaModelConverter.ts +5 -2
- package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
- package/src/sap/fe/core/converters/TemplateConverter.ts +2 -1
- package/src/sap/fe/core/converters/annotations/DataField.js +26 -12
- package/src/sap/fe/core/converters/annotations/DataField.ts +37 -13
- package/src/sap/fe/core/converters/controls/Common/Form.js +2 -2
- package/src/sap/fe/core/converters/controls/Common/Form.ts +4 -2
- package/src/sap/fe/core/converters/controls/Common/KPI.js +1 -1
- package/src/sap/fe/core/converters/controls/Common/KPI.ts +3 -2
- package/src/sap/fe/core/converters/controls/Common/Table.js +130 -23
- package/src/sap/fe/core/converters/controls/Common/Table.ts +134 -29
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +118 -53
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +156 -93
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +161 -207
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +148 -206
- package/src/sap/fe/core/converters/helpers/Aggregation.js +115 -133
- package/src/sap/fe/core/converters/helpers/BindingHelper.js +20 -6
- package/src/sap/fe/core/converters/helpers/BindingHelper.ts +16 -4
- package/src/sap/fe/core/converters/helpers/ConfigurableObject.js +12 -1
- package/src/sap/fe/core/converters/helpers/ConfigurableObject.ts +11 -0
- package/src/sap/fe/core/converters/helpers/Key.js +42 -57
- package/src/sap/fe/core/converters/helpers/Key.ts +1 -1
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +25 -4
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +20 -3
- package/src/sap/fe/core/converters/templates/ListReportConverter.js +10 -4
- package/src/sap/fe/core/converters/templates/ListReportConverter.ts +15 -4
- package/src/sap/fe/core/converters/templates/ObjectPageConverter.js +7 -5
- package/src/sap/fe/core/converters/templates/ObjectPageConverter.ts +5 -1
- package/src/sap/fe/core/designtime/AppComponent.designtime.js +92 -98
- package/src/sap/fe/core/designtime/AppComponent.designtime.ts +91 -0
- package/src/sap/fe/core/formatters/CollaborationFormatter.js +104 -0
- package/src/sap/fe/core/formatters/CollaborationFormatter.ts +60 -0
- package/src/sap/fe/core/formatters/TableFormatter.js +53 -2
- package/src/sap/fe/core/formatters/TableFormatter.ts +51 -0
- package/src/sap/fe/core/fpm/Component.js +50 -54
- package/src/sap/fe/core/fpm/Component.ts +48 -0
- package/src/sap/fe/core/helpers/AppStartupHelper.js +323 -309
- package/src/sap/fe/core/helpers/AppStartupHelper.ts +363 -337
- package/src/sap/fe/core/helpers/BindingExpression.js +7 -7
- package/src/sap/fe/core/helpers/BindingExpression.ts +7 -7
- package/src/sap/fe/core/helpers/ClassSupport.js +186 -62
- package/src/sap/fe/core/helpers/ClassSupport.ts +168 -52
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +5 -4
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +1 -5
- package/src/sap/fe/core/helpers/FPMHelper.js +1 -1
- package/src/sap/fe/core/helpers/FPMHelper.ts +1 -1
- package/src/sap/fe/core/helpers/MassEditHelper.js +601 -684
- package/src/sap/fe/core/helpers/MassEditHelper.ts +699 -0
- package/src/sap/fe/core/helpers/ModelHelper.js +25 -1
- package/src/sap/fe/core/helpers/ModelHelper.ts +23 -0
- package/src/sap/fe/core/helpers/SemanticDateOperators.js +5 -1
- package/src/sap/fe/core/helpers/SemanticDateOperators.ts +4 -0
- package/src/sap/fe/core/library.js +426 -451
- package/src/sap/fe/core/library.support.js +22 -33
- package/src/sap/fe/core/library.support.ts +23 -0
- package/src/sap/fe/core/library.ts +420 -0
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +5 -3
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +4 -1
- package/src/sap/fe/core/messagebundle.properties +22 -5
- package/src/sap/fe/core/messagebundle_ar.properties +27 -7
- package/src/sap/fe/core/messagebundle_bg.properties +27 -7
- package/src/sap/fe/core/messagebundle_ca.properties +27 -7
- package/src/sap/fe/core/messagebundle_cs.properties +27 -7
- package/src/sap/fe/core/messagebundle_cy.properties +27 -7
- package/src/sap/fe/core/messagebundle_da.properties +27 -7
- package/src/sap/fe/core/messagebundle_de.properties +27 -7
- package/src/sap/fe/core/messagebundle_el.properties +27 -7
- package/src/sap/fe/core/messagebundle_en.properties +27 -7
- package/src/sap/fe/core/messagebundle_en_GB.properties +27 -7
- package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +27 -7
- package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +27 -7
- package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +27 -7
- package/src/sap/fe/core/messagebundle_es.properties +27 -7
- package/src/sap/fe/core/messagebundle_es_MX.properties +27 -7
- package/src/sap/fe/core/messagebundle_et.properties +27 -7
- package/src/sap/fe/core/messagebundle_fi.properties +28 -8
- package/src/sap/fe/core/messagebundle_fr.properties +28 -8
- package/src/sap/fe/core/messagebundle_fr_CA.properties +29 -9
- package/src/sap/fe/core/messagebundle_hi.properties +27 -7
- package/src/sap/fe/core/messagebundle_hr.properties +27 -7
- package/src/sap/fe/core/messagebundle_hu.properties +27 -7
- package/src/sap/fe/core/messagebundle_id.properties +27 -7
- package/src/sap/fe/core/messagebundle_it.properties +27 -7
- package/src/sap/fe/core/messagebundle_iw.properties +27 -7
- package/src/sap/fe/core/messagebundle_ja.properties +26 -6
- package/src/sap/fe/core/messagebundle_kk.properties +27 -7
- package/src/sap/fe/core/messagebundle_ko.properties +27 -7
- package/src/sap/fe/core/messagebundle_lt.properties +27 -7
- package/src/sap/fe/core/messagebundle_lv.properties +28 -8
- package/src/sap/fe/core/messagebundle_ms.properties +27 -7
- package/src/sap/fe/core/messagebundle_nl.properties +27 -7
- package/src/sap/fe/core/messagebundle_no.properties +27 -7
- package/src/sap/fe/core/messagebundle_pl.properties +27 -7
- package/src/sap/fe/core/messagebundle_pt.properties +28 -8
- package/src/sap/fe/core/messagebundle_pt_PT.properties +27 -7
- package/src/sap/fe/core/messagebundle_ro.properties +27 -7
- package/src/sap/fe/core/messagebundle_ru.properties +27 -7
- package/src/sap/fe/core/messagebundle_sh.properties +27 -7
- package/src/sap/fe/core/messagebundle_sk.properties +27 -7
- package/src/sap/fe/core/messagebundle_sl.properties +27 -7
- package/src/sap/fe/core/messagebundle_sv.properties +27 -7
- package/src/sap/fe/core/messagebundle_th.properties +26 -6
- package/src/sap/fe/core/messagebundle_tr.properties +30 -10
- package/src/sap/fe/core/messagebundle_uk.properties +27 -7
- package/src/sap/fe/core/messagebundle_vi.properties +27 -7
- package/src/sap/fe/core/messagebundle_zh_CN.properties +27 -7
- package/src/sap/fe/core/messagebundle_zh_TW.properties +27 -7
- package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +45 -71
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +154 -192
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +4 -4
- package/src/sap/fe/core/services/EnvironmentServiceFactory.js +66 -92
- package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +1 -1
- package/src/sap/fe/core/services/NavigationServiceFactory.js +284 -339
- package/src/sap/fe/core/services/NavigationServiceFactory.ts +10 -13
- package/src/sap/fe/core/services/ResourceModelServiceFactory.js +67 -102
- package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +5 -2
- package/src/sap/fe/core/services/RoutingServiceFactory.js +754 -814
- package/src/sap/fe/core/services/RoutingServiceFactory.ts +13 -13
- package/src/sap/fe/core/services/ShellServicesFactory.js +649 -736
- package/src/sap/fe/core/services/ShellServicesFactory.ts +7 -4
- package/src/sap/fe/core/services/SideEffectsServiceFactory.js +567 -592
- package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +22 -3
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +354 -386
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +21 -14
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +1 -1
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +1 -1
- package/src/sap/fe/core/support/CommonHelper.js +1 -1
- package/src/sap/fe/core/support/CommonHelper.ts +1 -1
- package/src/sap/fe/core/support/Diagnostics.js +36 -48
- package/src/sap/fe/core/templating/DataModelPathHelper.js +93 -85
- package/src/sap/fe/core/templating/DataModelPathHelper.ts +104 -95
- package/src/sap/fe/core/templating/FilterHelper.js +25 -32
- package/src/sap/fe/core/templating/FilterHelper.ts +36 -35
- package/src/sap/fe/core/templating/PropertyHelper.js +2 -2
- package/src/sap/fe/core/templating/PropertyHelper.ts +1 -1
- package/src/sap/fe/core/templating/UIFormatters.js +32 -26
- package/src/sap/fe/core/templating/UIFormatters.ts +37 -24
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import Event from "sap/ui/base/Event";
|
|
2
|
+
import Control from "sap/ui/core/Control";
|
|
3
|
+
import SapPcpWebSocket from "sap/ui/core/ws/SapPcpWebSocket";
|
|
4
|
+
import MessageBox from "sap/m/MessageBox";
|
|
5
|
+
import { Activity, CollaborationUtils, Message, User, UserActivity } from "sap/fe/core/actions/collaboration/CollaborationCommon";
|
|
6
|
+
import View from "sap/ui/core/mvc/View";
|
|
7
|
+
import UriParameters from "sap/base/util/UriParameters";
|
|
8
|
+
import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
|
|
9
|
+
import Log from "sap/base/Log";
|
|
10
|
+
import Context from "sap/ui/model/odata/v4/Context";
|
|
11
|
+
import ResourceBundle from "sap/base/i18n/ResourceBundle";
|
|
12
|
+
|
|
13
|
+
const CONNECTED = "/collaboration/connected";
|
|
14
|
+
const CONNECTION = "/collaboration/connection";
|
|
15
|
+
const MYACTIVITY = "/collaboration/myActivity";
|
|
16
|
+
const ACTIVEUSERS = "/collaboration/activeUsers";
|
|
17
|
+
const ACTIVITIES = "/collaboration/activities";
|
|
18
|
+
|
|
19
|
+
export const isConnected = function(control: Control): boolean {
|
|
20
|
+
return !!control.getModel("internal").getProperty(CONNECTED);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const send = function(control: Control, action: Activity, content: string | string[] | Context | Context[] | undefined) {
|
|
24
|
+
if (isConnected(control)) {
|
|
25
|
+
const contentArray = Array.isArray(content) ? content : [content];
|
|
26
|
+
let clientContent: string = "";
|
|
27
|
+
contentArray.forEach(function(c) {
|
|
28
|
+
if (c) {
|
|
29
|
+
clientContent += clientContent ? "|" : "";
|
|
30
|
+
clientContent += typeof c === "string" ? c : c.getPath();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const internalModel = control.getModel("internal") as any;
|
|
35
|
+
const webSocket = internalModel.getProperty(CONNECTION) as SapPcpWebSocket;
|
|
36
|
+
|
|
37
|
+
if (action === Activity.LiveChange) {
|
|
38
|
+
// To avoid unnecessary traffic we keep track of live changes and send it only once
|
|
39
|
+
const myActivity = internalModel.getProperty(MYACTIVITY);
|
|
40
|
+
if (myActivity === clientContent) {
|
|
41
|
+
return;
|
|
42
|
+
} else {
|
|
43
|
+
internalModel.setProperty(MYACTIVITY, clientContent);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
// user finished the activity
|
|
47
|
+
internalModel.setProperty(MYACTIVITY, null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
webSocket.send("", {
|
|
51
|
+
clientAction: action,
|
|
52
|
+
clientContent: clientContent
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (action === Activity.Activate || action === Activity.Discard) {
|
|
56
|
+
disconnect(control);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const connect = async function(view: View) {
|
|
62
|
+
const internalModel: any = view.getModel("internal");
|
|
63
|
+
const me = CollaborationUtils.getMe(view);
|
|
64
|
+
if (internalModel.getProperty(CONNECTION)) {
|
|
65
|
+
// connection already established
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Retrieving ME from shell service
|
|
70
|
+
if (!me) {
|
|
71
|
+
// no me = no shell = not sure what to do
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const activeUsers: any[] = [me];
|
|
76
|
+
internalModel.setProperty("/collaboration", { activeUsers: activeUsers, activities: {} });
|
|
77
|
+
const bindingContext = view.getBindingContext() as Context;
|
|
78
|
+
const webSocketBaseURL = bindingContext
|
|
79
|
+
.getModel()
|
|
80
|
+
.getMetaModel()
|
|
81
|
+
.getObject("/@com.sap.vocabularies.Common.v1.WebSocketBaseURL");
|
|
82
|
+
|
|
83
|
+
if (!webSocketBaseURL) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sDraftUUID = await bindingContext.requestProperty("DraftAdministrativeData/DraftUUID");
|
|
88
|
+
if (!sDraftUUID) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const webSocket = establishWebSocketConnection(webSocketBaseURL, sDraftUUID, view);
|
|
93
|
+
|
|
94
|
+
webSocket.attachMessage(function(event: Event) {
|
|
95
|
+
const message: Message = event.getParameter("pcpFields");
|
|
96
|
+
messageReceive(message, view, webSocket);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const disconnect = function(control: Control) {
|
|
101
|
+
const internalModel = control.getModel("internal") as any;
|
|
102
|
+
const webSocket = internalModel.getProperty(CONNECTION) as SapPcpWebSocket;
|
|
103
|
+
webSocket.close();
|
|
104
|
+
internalModel.setProperty(CONNECTION, null);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
function establishWebSocketConnection(socketBaseURL: string, draftUUID: string, view: View) {
|
|
108
|
+
const internalModel: any = view.getModel("internal");
|
|
109
|
+
const hostLocation = window.location;
|
|
110
|
+
let socketURI;
|
|
111
|
+
|
|
112
|
+
// Support useBackendUrl for local testing
|
|
113
|
+
const useBackendUrl = UriParameters.fromQuery(window.location.search).get("useBackendUrl");
|
|
114
|
+
if (useBackendUrl) {
|
|
115
|
+
socketURI = useBackendUrl.replace("https", "wss");
|
|
116
|
+
} else {
|
|
117
|
+
socketURI = hostLocation.protocol === "https:" ? "wss:" : "ws:";
|
|
118
|
+
socketURI += "//" + hostLocation.host;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
socketURI += (socketBaseURL.startsWith("/") ? "" : "/") + socketBaseURL + "?draft=" + draftUUID;
|
|
122
|
+
|
|
123
|
+
const webSocket = new SapPcpWebSocket(socketURI, ["v10.pcp.sap.com"]); // TODO: use enum
|
|
124
|
+
internalModel.setProperty(CONNECTION, webSocket);
|
|
125
|
+
|
|
126
|
+
webSocket.attachOpen(function() {
|
|
127
|
+
internalModel.setProperty(CONNECTED, true);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
webSocket.attachError(function() {
|
|
131
|
+
Log.error("The connection to the websocket channel " + socketBaseURL + " could not be established");
|
|
132
|
+
internalModel.setProperty(CONNECTED, false);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
webSocket.attachClose(function() {
|
|
136
|
+
internalModel.setProperty(CONNECTED, false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return webSocket;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function messageReceive(message: Message, view: View, webSocket: any) {
|
|
143
|
+
const internalModel: any = view.getModel("internal");
|
|
144
|
+
let activeUsers: User[] = internalModel.getProperty(ACTIVEUSERS);
|
|
145
|
+
let activities: UserActivity[];
|
|
146
|
+
let activityKey: string;
|
|
147
|
+
const metaPath: string = message.clientContent && (view.getModel().getMetaModel() as ODataMetaModel).getMetaPath(message.clientContent);
|
|
148
|
+
message.userAction = message.userAction || message.clientAction;
|
|
149
|
+
|
|
150
|
+
const sender: User = {
|
|
151
|
+
id: message.userID,
|
|
152
|
+
name: message.userDescription,
|
|
153
|
+
initials: CollaborationUtils.formatInitials(message.userDescription),
|
|
154
|
+
color: CollaborationUtils.getUserColor(message.userID, activeUsers, [])
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
switch (message.userAction) {
|
|
158
|
+
case Activity.Join:
|
|
159
|
+
case Activity.JoinEcho:
|
|
160
|
+
if (activeUsers.findIndex(user => user.id === sender.id) === -1) {
|
|
161
|
+
activeUsers.push(sender);
|
|
162
|
+
internalModel.setProperty(ACTIVEUSERS, activeUsers);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (message.userAction === Activity.Join) {
|
|
166
|
+
// we echo our existence to the newly entered user and also send the current activity if there is any
|
|
167
|
+
webSocket.send("", {
|
|
168
|
+
clientAction: Activity.JoinEcho,
|
|
169
|
+
clientContent: internalModel.getProperty(MYACTIVITY)
|
|
170
|
+
});
|
|
171
|
+
// TODO: echo current activity
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (message.userAction === Activity.JoinEcho) {
|
|
175
|
+
if (message.clientContent) {
|
|
176
|
+
// another user was already typing therefore I want to see his activity immediately. Calling me again as a live change
|
|
177
|
+
message.userAction = Activity.LiveChange;
|
|
178
|
+
messageReceive(message, view, webSocket);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
break;
|
|
183
|
+
case Activity.Leave:
|
|
184
|
+
// Removing the active user. Not removing "me" if I had the screen open in another session
|
|
185
|
+
activeUsers = activeUsers.filter(user => user.id !== sender.id || user.me);
|
|
186
|
+
internalModel.setProperty(ACTIVEUSERS, activeUsers);
|
|
187
|
+
const allActivities = internalModel.getProperty(ACTIVITIES) || {};
|
|
188
|
+
const removeUserActivities = function(bag: any) {
|
|
189
|
+
if (Array.isArray(bag)) {
|
|
190
|
+
return bag.filter(activity => activity.id !== sender.id);
|
|
191
|
+
} else {
|
|
192
|
+
for (const p in bag) {
|
|
193
|
+
bag[p] = removeUserActivities(bag[p]);
|
|
194
|
+
}
|
|
195
|
+
return bag;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
removeUserActivities(allActivities);
|
|
199
|
+
internalModel.setProperty(ACTIVITIES, allActivities);
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case Activity.Change:
|
|
203
|
+
let currentActivities: any[] = internalModel.getProperty(ACTIVITIES + metaPath) || [];
|
|
204
|
+
activityKey = getActivityKey(message.clientContent);
|
|
205
|
+
currentActivities = currentActivities.filter(activity => activity.key !== activityKey);
|
|
206
|
+
internalModel.setProperty(ACTIVITIES + metaPath, currentActivities);
|
|
207
|
+
|
|
208
|
+
update(view, message, metaPath, Activity.Change);
|
|
209
|
+
break;
|
|
210
|
+
case Activity.Create:
|
|
211
|
+
// For create we actually just need to refresh the table
|
|
212
|
+
update(view, message, metaPath, Activity.Create);
|
|
213
|
+
break;
|
|
214
|
+
case Activity.Delete:
|
|
215
|
+
// For now also refresh the page but in case of deletion we need to inform the user
|
|
216
|
+
update(view, message, metaPath, Activity.Delete);
|
|
217
|
+
break;
|
|
218
|
+
case Activity.Activate:
|
|
219
|
+
disconnect(view);
|
|
220
|
+
MessageBox.information(getText("C_COLLABORATIONDRAFT_ACTIVATE", sender.name));
|
|
221
|
+
navigate(message.clientContent, view);
|
|
222
|
+
break;
|
|
223
|
+
case Activity.Discard:
|
|
224
|
+
disconnect(view);
|
|
225
|
+
MessageBox.information(getText("C_COLLABORATIONDRAFT_DISCARD", sender.name));
|
|
226
|
+
navigate(message.clientContent, view);
|
|
227
|
+
break;
|
|
228
|
+
/*
|
|
229
|
+
// TODO: Action to be implemented
|
|
230
|
+
case Activity.Action:
|
|
231
|
+
// Just for test reasons show a toast - to be checked with UX
|
|
232
|
+
MessageToast.show("User " + sender.name + " has executed action " + metaPath.split("|")[0]);
|
|
233
|
+
//update(view, message, metaPath, Activity.Delete);
|
|
234
|
+
break;
|
|
235
|
+
*/
|
|
236
|
+
case Activity.LiveChange:
|
|
237
|
+
const activity: UserActivity = sender;
|
|
238
|
+
activity.key = getActivityKey(message.clientContent);
|
|
239
|
+
|
|
240
|
+
// stupid JSON model...
|
|
241
|
+
let initJSONModel: string = "";
|
|
242
|
+
const parts = metaPath.split("/");
|
|
243
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
244
|
+
initJSONModel += "/" + parts[i];
|
|
245
|
+
if (!internalModel.getProperty(ACTIVITIES + initJSONModel)) {
|
|
246
|
+
internalModel.setProperty(ACTIVITIES + initJSONModel, {});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
activities = internalModel.getProperty(ACTIVITIES + metaPath);
|
|
251
|
+
activities = activities ? activities.slice() : [];
|
|
252
|
+
activities.push(activity);
|
|
253
|
+
internalModel.setProperty(ACTIVITIES + metaPath, activities);
|
|
254
|
+
break;
|
|
255
|
+
case Activity.Undo:
|
|
256
|
+
// The user did a change but reverted it, therefore unblock the control
|
|
257
|
+
activities = internalModel.getProperty(ACTIVITIES + metaPath);
|
|
258
|
+
activityKey = getActivityKey(message.clientContent);
|
|
259
|
+
internalModel.setProperty(
|
|
260
|
+
ACTIVITIES + metaPath,
|
|
261
|
+
activities.filter(a => a.key !== activityKey)
|
|
262
|
+
);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function update(view: View, message: Message, metaPath: string, action: Activity) {
|
|
268
|
+
const appComponent = CollaborationUtils.getAppComponent(view);
|
|
269
|
+
const metaModel = view.getModel().getMetaModel() as ODataMetaModel;
|
|
270
|
+
const currentPage = getCurrentPage(view);
|
|
271
|
+
const sideEffectsService = (appComponent as any).getSideEffectsService(); // TODO: add to appcomponent
|
|
272
|
+
const currentContext = currentPage.getBindingContext();
|
|
273
|
+
const currentPath = currentContext.getPath();
|
|
274
|
+
const currentMetaPath = metaModel.getMetaPath(currentPath);
|
|
275
|
+
let changedDocument = message.clientContent;
|
|
276
|
+
|
|
277
|
+
if (action === Activity.Delete) {
|
|
278
|
+
// check if user currently displays one deleted object
|
|
279
|
+
const deletedObjects = message.clientContent.split("|");
|
|
280
|
+
if (deletedObjects.findIndex(deletedObject => currentPath.startsWith(deletedObject)) > -1) {
|
|
281
|
+
// any other user deleted the object I'm currently looking at. Inform the user we will navigate to root now
|
|
282
|
+
MessageBox.information(getText("C_COLLABORATIONDRAFT_DELETE", message.userDescription), {
|
|
283
|
+
onClose: function() {
|
|
284
|
+
// TODO: Navigate to root? To be implemented
|
|
285
|
+
history.back();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// TODO: For now just take the first object to get the meta path and do a full refresh of the table
|
|
290
|
+
changedDocument = deletedObjects[0];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (changedDocument.startsWith(currentPath)) {
|
|
294
|
+
// Execute SideEffects (TODO for Meet there should be one central method)
|
|
295
|
+
const activityPath = metaPath.replace(currentMetaPath, "").slice(1);
|
|
296
|
+
if (activityPath) {
|
|
297
|
+
// Request also the property itself
|
|
298
|
+
const sideEffects: any[] = [
|
|
299
|
+
{
|
|
300
|
+
$PropertyPath: activityPath
|
|
301
|
+
}
|
|
302
|
+
];
|
|
303
|
+
const entityType = sideEffectsService.getEntityTypeFromContext(currentContext);
|
|
304
|
+
const entityTypeSideEffects = sideEffectsService.getODataEntitySideEffects(entityType);
|
|
305
|
+
// Poor man solution without checking source targets, just for POC, this is throw-way coding only
|
|
306
|
+
const object: any = Object; // just to overcome TS issues, will be anyway replaced
|
|
307
|
+
const relevantSideEffects = object.fromEntries(
|
|
308
|
+
object
|
|
309
|
+
.entries(entityTypeSideEffects)
|
|
310
|
+
.filter((x: any[]) => x[1].SourceProperties?.findIndex((source: any) => source.value === activityPath) > -1)
|
|
311
|
+
);
|
|
312
|
+
for (const p in relevantSideEffects) {
|
|
313
|
+
relevantSideEffects[p].TargetProperties.forEach(function(targetProperty: any) {
|
|
314
|
+
sideEffects.push({
|
|
315
|
+
$PropertyPath: targetProperty
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
sideEffectsService.requestSideEffects(sideEffects, currentContext, "$auto");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Simulate any change so the edit flow shows the draft indicator and sets the page to dirty
|
|
324
|
+
currentPage.getController().editFlow.updateDocument(currentContext, Promise.resolve());
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function navigate(path: string, view: View) {
|
|
328
|
+
// TODO: routing.navigate doesn't consider semantic bookmarking
|
|
329
|
+
const currentPage = getCurrentPage(view);
|
|
330
|
+
const targetContext = view
|
|
331
|
+
.getModel()
|
|
332
|
+
.bindContext(path)
|
|
333
|
+
.getBoundContext();
|
|
334
|
+
currentPage.getController().routing.navigate(targetContext);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function getCurrentPage(view: View) {
|
|
338
|
+
const appComponent = CollaborationUtils.getAppComponent(view);
|
|
339
|
+
// TODO: isn't there an easier way to get the current page?
|
|
340
|
+
// TODO: What about FCL?
|
|
341
|
+
return (appComponent.getRootControl() as any)
|
|
342
|
+
.getContent()[0]
|
|
343
|
+
.getCurrentPage()
|
|
344
|
+
.getComponentInstance()
|
|
345
|
+
.getRootControl();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function getActivityKey(x: string): string {
|
|
349
|
+
return x.substring(x.lastIndexOf("(") + 1, x.lastIndexOf(")"));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getText(textId: string, ...args: string[]): string {
|
|
353
|
+
const oResourceModel = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core") as ResourceBundle;
|
|
354
|
+
return oResourceModel.getText(textId, args);
|
|
355
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SAP UI development toolkit for HTML5 (SAPUI5)
|
|
3
|
+
* (c) Copyright 2009-2021 SAP SE. All rights reserved
|
|
4
|
+
*/
|
|
5
|
+
sap.ui.define(["sap/ui/core/Component"], function (Component) {
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
var _exports = {};
|
|
9
|
+
|
|
10
|
+
var Role;
|
|
11
|
+
|
|
12
|
+
(function (Role) {
|
|
13
|
+
Role["Owner"] = "O";
|
|
14
|
+
Role["Edit"] = "E";
|
|
15
|
+
})(Role || (Role = {}));
|
|
16
|
+
|
|
17
|
+
_exports.Role = Role;
|
|
18
|
+
var Activity;
|
|
19
|
+
|
|
20
|
+
(function (Activity) {
|
|
21
|
+
Activity["Join"] = "JOIN";
|
|
22
|
+
Activity["JoinEcho"] = "JOINECHO";
|
|
23
|
+
Activity["Leave"] = "LEAVE";
|
|
24
|
+
Activity["Change"] = "CHANGE";
|
|
25
|
+
Activity["Create"] = "CREATE";
|
|
26
|
+
Activity["Delete"] = "DELETE";
|
|
27
|
+
Activity["Action"] = "ACTION";
|
|
28
|
+
Activity["LiveChange"] = "LIVECHANGE";
|
|
29
|
+
Activity["Activate"] = "ACTIVATE";
|
|
30
|
+
Activity["Discard"] = "DISCARD";
|
|
31
|
+
Activity["Undo"] = "UNDO";
|
|
32
|
+
})(Activity || (Activity = {}));
|
|
33
|
+
|
|
34
|
+
_exports.Activity = Activity;
|
|
35
|
+
|
|
36
|
+
function formatInitials(fullName) {
|
|
37
|
+
// remove titles - those are the ones from S/4 to be checked if there are others
|
|
38
|
+
var academicTitles = ["Dr.", "Prof.", "Prof. Dr.", "B.A.", "MBA", "Ph.D."];
|
|
39
|
+
academicTitles.forEach(function (academicTitle) {
|
|
40
|
+
fullName = fullName.replace(academicTitle, "");
|
|
41
|
+
});
|
|
42
|
+
var initials;
|
|
43
|
+
var parts = fullName.trimStart().split(" ");
|
|
44
|
+
|
|
45
|
+
if (parts.length > 1) {
|
|
46
|
+
var _parts$shift, _parts$pop;
|
|
47
|
+
|
|
48
|
+
initials = ((parts === null || parts === void 0 ? void 0 : (_parts$shift = parts.shift()) === null || _parts$shift === void 0 ? void 0 : _parts$shift.charAt(0)) || "") + ((_parts$pop = parts.pop()) === null || _parts$pop === void 0 ? void 0 : _parts$pop.charAt(0));
|
|
49
|
+
} else {
|
|
50
|
+
initials = fullName.substring(0, 2);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return initials.toUpperCase();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getUserColor(UserID, activeUsers, invitedUsers) {
|
|
57
|
+
// search if user is known
|
|
58
|
+
var user = activeUsers.find(function (u) {
|
|
59
|
+
return u.id === UserID;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (user) {
|
|
63
|
+
return user.color;
|
|
64
|
+
} else {
|
|
65
|
+
var _loop = function (i) {
|
|
66
|
+
if (activeUsers.findIndex(function (u) {
|
|
67
|
+
return u.color === i;
|
|
68
|
+
}) === -1 && invitedUsers.findIndex(function (u) {
|
|
69
|
+
return u.color === i;
|
|
70
|
+
}) === -1) {
|
|
71
|
+
return {
|
|
72
|
+
v: i
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// search for next free color
|
|
78
|
+
for (var i = 1; i <= 10; i++) {
|
|
79
|
+
var _ret = _loop(i);
|
|
80
|
+
|
|
81
|
+
if (typeof _ret === "object") return _ret.v;
|
|
82
|
+
} // this seems to be a popular object :) for now just return 10 for all.
|
|
83
|
+
// for invited we should start from 1 again so the colors are different
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
return 10;
|
|
87
|
+
}
|
|
88
|
+
} // copied from CommonUtils. Due to a cycle dependency I can't use CommonUtils here.
|
|
89
|
+
// That's to be fixed. the discard popover thingy shouldn't be in the common utils at all
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
function getAppComponent(oControl) {
|
|
93
|
+
if (oControl.isA("sap.fe.core.AppComponent")) {
|
|
94
|
+
return oControl;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var oOwner = Component.getOwnerComponentFor(oControl);
|
|
98
|
+
|
|
99
|
+
if (!oOwner) {
|
|
100
|
+
return oControl;
|
|
101
|
+
} else {
|
|
102
|
+
return getAppComponent(oOwner);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getMe(view) {
|
|
107
|
+
var shellServiceHelper = getAppComponent(view).getShellServices();
|
|
108
|
+
|
|
109
|
+
if (!shellServiceHelper || !shellServiceHelper.hasUShell()) {
|
|
110
|
+
throw "No Shell... No User";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
var me = {
|
|
114
|
+
initials: shellServiceHelper.getUser().getInitials(),
|
|
115
|
+
id: shellServiceHelper.getUser().getId(),
|
|
116
|
+
name: shellServiceHelper.getUser().getFullName(),
|
|
117
|
+
color: 6,
|
|
118
|
+
// same color as FLP...
|
|
119
|
+
role: Role.Owner,
|
|
120
|
+
me: true
|
|
121
|
+
};
|
|
122
|
+
me.name += " (You)"; // TODO resource bundle
|
|
123
|
+
|
|
124
|
+
return me;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var CollaborationUtils = {
|
|
128
|
+
formatInitials: formatInitials,
|
|
129
|
+
getUserColor: getUserColor,
|
|
130
|
+
getMe: getMe,
|
|
131
|
+
getAppComponent: getAppComponent
|
|
132
|
+
};
|
|
133
|
+
_exports.CollaborationUtils = CollaborationUtils;
|
|
134
|
+
return _exports;
|
|
135
|
+
}, false);
|
|
136
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbGxhYm9yYXRpb25Db21tb24udHMiXSwibmFtZXMiOlsiUm9sZSIsIkFjdGl2aXR5IiwiZm9ybWF0SW5pdGlhbHMiLCJmdWxsTmFtZSIsImFjYWRlbWljVGl0bGVzIiwiZm9yRWFjaCIsImFjYWRlbWljVGl0bGUiLCJyZXBsYWNlIiwiaW5pdGlhbHMiLCJwYXJ0cyIsInRyaW1TdGFydCIsInNwbGl0IiwibGVuZ3RoIiwic2hpZnQiLCJjaGFyQXQiLCJwb3AiLCJzdWJzdHJpbmciLCJ0b1VwcGVyQ2FzZSIsImdldFVzZXJDb2xvciIsIlVzZXJJRCIsImFjdGl2ZVVzZXJzIiwiaW52aXRlZFVzZXJzIiwidXNlciIsImZpbmQiLCJ1IiwiaWQiLCJjb2xvciIsImkiLCJmaW5kSW5kZXgiLCJnZXRBcHBDb21wb25lbnQiLCJvQ29udHJvbCIsImlzQSIsIm9Pd25lciIsIkNvbXBvbmVudCIsImdldE93bmVyQ29tcG9uZW50Rm9yIiwiZ2V0TWUiLCJ2aWV3Iiwic2hlbGxTZXJ2aWNlSGVscGVyIiwiZ2V0U2hlbGxTZXJ2aWNlcyIsImhhc1VTaGVsbCIsIm1lIiwiZ2V0VXNlciIsImdldEluaXRpYWxzIiwiZ2V0SWQiLCJuYW1lIiwiZ2V0RnVsbE5hbWUiLCJyb2xlIiwiT3duZXIiLCJDb2xsYWJvcmF0aW9uVXRpbHMiXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7Ozs7OztNQWdCWUEsSTs7YUFBQUEsSTtBQUFBQSxJQUFBQSxJO0FBQUFBLElBQUFBLEk7S0FBQUEsSSxLQUFBQSxJOzs7TUFLQUMsUTs7YUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7S0FBQUEsUSxLQUFBQSxROzs7O0FBc0JaLFdBQVNDLGNBQVQs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QUFDRSxTQUFuQixFQUE1QixFQUE0RDtBQUMzRCxZQUFNLHFCQUFOO0FBQ0E7O0FBQ0QsUUFBTUMsRUFBUSxHQUFHO0FBQ2hCaEMsTUFBQUEsUUFBUSxFQUFFNkIsa0JBQWtCLENBQUNJLE9BQW5CLEdBQTZCQyxXQUE3QixFQURNO0FBRWhCakIsTUFBQUEsRUFBRSxFQUFFWSxrQkFBa0IsQ0FBQ0ksT0FBbkIsR0FBNkJFLEtBQTdCLEVBRlk7QUFHaEJDLE1BQUFBLElBQUksRUFBRVAsa0JBQWtCLENBQUNJLE9BQW5CLEdBQTZCSSxXQUE3QixFQUhVO0FBSWhCbkIsTUFBQUEsS0FBSyxFQUFFLENBSlM7QUFJTjtBQUNWb0IsTUFBQUEsSUFBSSxFQUFFOUMsSUFBSSxDQUFDK0MsS0FMSztBQU1oQlAsTUFBQUEsRUFBRSxFQUFFO0FBTlksS0FBakI7QUFRQUEsSUFBQUEsRUFBRSxDQUFDSSxJQUFILElBQVcsUUFBWCxDQWJnQyxDQWFYOztBQUNyQixXQUFPSixFQUFQO0FBQ0E7O0FBRU0sTUFBTVEsa0JBQWtCLEdBQUc7QUFDakM5QyxJQUFBQSxjQUFjLEVBQUVBLGNBRGlCO0FBRWpDZ0IsSUFBQUEsWUFBWSxFQUFFQSxZQUZtQjtBQUdqQ2lCLElBQUFBLEtBQUssRUFBRUEsS0FIMEI7QUFJakNOLElBQUFBLGVBQWUsRUFBRUE7QUFKZ0IsR0FBM0IiLCJzb3VyY2VSb290IjoiLiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBWaWV3IGZyb20gXCJzYXAvdWkvY29yZS9tdmMvVmlld1wiO1xuaW1wb3J0IENvbXBvbmVudCBmcm9tIFwic2FwL3VpL2NvcmUvQ29tcG9uZW50XCI7XG5pbXBvcnQgQXBwQ29tcG9uZW50IGZyb20gXCJzYXAvZmUvY29yZS9BcHBDb21wb25lbnRcIjtcblxuZXhwb3J0IHR5cGUgVXNlciA9IHtcblx0aWQ6IHN0cmluZztcblx0aW5pdGlhbHM/OiBzdHJpbmc7XG5cdG5hbWU6IHN0cmluZztcblx0cm9sZT86IFJvbGU7XG5cdGNvbG9yPzogbnVtYmVyO1xuXHR0cmFuc2llbnQ/OiBib29sZWFuO1xuXHRtZT86IGJvb2xlYW47XG59O1xuXG5leHBvcnQgdHlwZSBVc2VyQWN0aXZpdHkgPSBVc2VyICYge1xuXHRrZXk/OiBzdHJpbmc7XG59O1xuXG5leHBvcnQgZW51bSBSb2xlIHtcblx0T3duZXIgPSBcIk9cIixcblx0RWRpdCA9IFwiRVwiXG59XG5cbmV4cG9ydCBlbnVtIEFjdGl2aXR5IHtcblx0Sm9pbiA9IFwiSk9JTlwiLFxuXHRKb2luRWNobyA9IFwiSk9JTkVDSE9cIixcblx0TGVhdmUgPSBcIkxFQVZFXCIsXG5cdENoYW5nZSA9IFwiQ0hBTkdFXCIsXG5cdENyZWF0ZSA9IFwiQ1JFQVRFXCIsXG5cdERlbGV0ZSA9IFwiREVMRVRFXCIsXG5cdEFjdGlvbiA9IFwiQUNUSU9OXCIsXG5cdExpdmVDaGFuZ2UgPSBcIkxJVkVDSEFOR0VcIixcblx0QWN0aXZhdGUgPSBcIkFDVElWQVRFXCIsXG5cdERpc2NhcmQgPSBcIkRJU0NBUkRcIixcblx0VW5kbyA9IFwiVU5ET1wiXG59XG5cbmV4cG9ydCB0eXBlIE1lc3NhZ2UgPSB7XG5cdHVzZXJEZXNjcmlwdGlvbjogc3RyaW5nO1xuXHR1c2VySUQ6IHN0cmluZztcblx0dXNlckFjdGlvbjogc3RyaW5nO1xuXHRjbGllbnRBY3Rpb246IHN0cmluZztcblx0Y2xpZW50Q29udGVudDogc3RyaW5nO1xufTtcblxuZnVuY3Rpb24gZm9ybWF0SW5pdGlhbHMoZnVsbE5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG5cdC8vIHJlbW92ZSB0aXRsZXMgLSB0aG9zZSBhcmUgdGhlIG9uZXMgZnJvbSBTLzQgdG8gYmUgY2hlY2tlZCBpZiB0aGVyZSBhcmUgb3RoZXJzXG5cdGNvbnN0IGFjYWRlbWljVGl0bGVzID0gW1wiRHIuXCIsIFwiUHJvZi5cIiwgXCJQcm9mLiBEci5cIiwgXCJCLkEuXCIsIFwiTUJBXCIsIFwiUGguRC5cIl07XG5cdGFjYWRlbWljVGl0bGVzLmZvckVhY2goZnVuY3Rpb24oYWNhZGVtaWNUaXRsZSkge1xuXHRcdGZ1bGxOYW1lID0gZnVsbE5hbWUucmVwbGFjZShhY2FkZW1pY1RpdGxlLCBcIlwiKTtcblx0fSk7XG5cblx0bGV0IGluaXRpYWxzOiBzdHJpbmc7XG5cdGNvbnN0IHBhcnRzID0gZnVsbE5hbWUudHJpbVN0YXJ0KCkuc3BsaXQoXCIgXCIpO1xuXG5cdGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7XG5cdFx0aW5pdGlhbHMgPSAocGFydHM/LnNoaWZ0KCk/LmNoYXJBdCgwKSB8fCBcIlwiKSArIHBhcnRzLnBvcCgpPy5jaGFyQXQoMCk7XG5cdH0gZWxzZSB7XG5cdFx0aW5pdGlhbHMgPSBmdWxsTmFtZS5zdWJzdHJpbmcoMCwgMik7XG5cdH1cblxuXHRyZXR1cm4gaW5pdGlhbHMudG9VcHBlckNhc2UoKTtcbn1cblxuZnVuY3Rpb24gZ2V0VXNlckNvbG9yKFVzZXJJRDogc3RyaW5nLCBhY3RpdmVVc2VyczogVXNlcltdLCBpbnZpdGVkVXNlcnM6IFVzZXJbXSkge1xuXHQvLyBzZWFyY2ggaWYgdXNlciBpcyBrbm93blxuXHRjb25zdCB1c2VyID0gYWN0aXZlVXNlcnMuZmluZCh1ID0+IHUuaWQgPT09IFVzZXJJRCk7XG5cdGlmICh1c2VyKSB7XG5cdFx0cmV0dXJuIHVzZXIuY29sb3I7XG5cdH0gZWxzZSB7XG5cdFx0Ly8gc2VhcmNoIGZvciBuZXh0IGZyZWUgY29sb3Jcblx0XHRmb3IgKGxldCBpID0gMTsgaSA8PSAxMDsgaSsrKSB7XG5cdFx0XHRpZiAoYWN0aXZlVXNlcnMuZmluZEluZGV4KHUgPT4gdS5jb2xvciA9PT0gaSkgPT09IC0xICYmIGludml0ZWRVc2Vycy5maW5kSW5kZXgodSA9PiB1LmNvbG9yID09PSBpKSA9PT0gLTEpIHtcblx0XHRcdFx0cmV0dXJuIGk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdC8vIHRoaXMgc2VlbXMgdG8gYmUgYSBwb3B1bGFyIG9iamVjdCA6KSBmb3Igbm93IGp1c3QgcmV0dXJuIDEwIGZvciBhbGwuXG5cdFx0Ly8gZm9yIGludml0ZWQgd2Ugc2hvdWxkIHN0YXJ0IGZyb20gMSBhZ2FpbiBzbyB0aGUgY29sb3JzIGFyZSBkaWZmZXJlbnRcblx0XHRyZXR1cm4gMTA7XG5cdH1cbn1cblxuLy8gY29waWVkIGZyb20gQ29tbW9uVXRpbHMuIER1ZSB0byBhIGN5Y2xlIGRlcGVuZGVuY3kgSSBjYW4ndCB1c2UgQ29tbW9uVXRpbHMgaGVyZS5cbi8vIFRoYXQncyB0byBiZSBmaXhlZC4gdGhlIGRpc2NhcmQgcG9wb3ZlciB0aGluZ3kgc2hvdWxkbid0IGJlIGluIHRoZSBjb21tb24gdXRpbHMgYXQgYWxsXG5mdW5jdGlvbiBnZXRBcHBDb21wb25lbnQob0NvbnRyb2w6IGFueSk6IEFwcENvbXBvbmVudCB7XG5cdGlmIChvQ29udHJvbC5pc0EoXCJzYXAuZmUuY29yZS5BcHBDb21wb25lbnRcIikpIHtcblx0XHRyZXR1cm4gb0NvbnRyb2w7XG5cdH1cblx0Y29uc3Qgb093bmVyID0gQ29tcG9uZW50LmdldE93bmVyQ29tcG9uZW50Rm9yKG9Db250cm9sKTtcblx0aWYgKCFvT3duZXIpIHtcblx0XHRyZXR1cm4gb0NvbnRyb2w7XG5cdH0gZWxzZSB7XG5cdFx0cmV0dXJuIGdldEFwcENvbXBvbmVudChvT3duZXIpO1xuXHR9XG59XG5cbmZ1bmN0aW9uIGdldE1lKHZpZXc6IFZpZXcpOiBVc2VyIHtcblx0Y29uc3Qgc2hlbGxTZXJ2aWNlSGVscGVyID0gZ2V0QXBwQ29tcG9uZW50KHZpZXcpLmdldFNoZWxsU2VydmljZXMoKTtcblx0aWYgKCFzaGVsbFNlcnZpY2VIZWxwZXIgfHwgIXNoZWxsU2VydmljZUhlbHBlci5oYXNVU2hlbGwoKSkge1xuXHRcdHRocm93IFwiTm8gU2hlbGwuLi4gTm8gVXNlclwiO1xuXHR9XG5cdGNvbnN0IG1lOiBVc2VyID0ge1xuXHRcdGluaXRpYWxzOiBzaGVsbFNlcnZpY2VIZWxwZXIuZ2V0VXNlcigpLmdldEluaXRpYWxzKCksXG5cdFx0aWQ6IHNoZWxsU2VydmljZUhlbHBlci5nZXRVc2VyKCkuZ2V0SWQoKSxcblx0XHRuYW1lOiBzaGVsbFNlcnZpY2VIZWxwZXIuZ2V0VXNlcigpLmdldEZ1bGxOYW1lKCksXG5cdFx0Y29sb3I6IDYsIC8vICBzYW1lIGNvbG9yIGFzIEZMUC4uLlxuXHRcdHJvbGU6IFJvbGUuT3duZXIsXG5cdFx0bWU6IHRydWVcblx0fTtcblx0bWUubmFtZSArPSBcIiAoWW91KVwiOyAvLyBUT0RPIHJlc291cmNlIGJ1bmRsZVxuXHRyZXR1cm4gbWU7XG59XG5cbmV4cG9ydCBjb25zdCBDb2xsYWJvcmF0aW9uVXRpbHMgPSB7XG5cdGZvcm1hdEluaXRpYWxzOiBmb3JtYXRJbml0aWFscyxcblx0Z2V0VXNlckNvbG9yOiBnZXRVc2VyQ29sb3IsXG5cdGdldE1lOiBnZXRNZSxcblx0Z2V0QXBwQ29tcG9uZW50OiBnZXRBcHBDb21wb25lbnRcbn07XG4iXX0=
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import View from "sap/ui/core/mvc/View";
|
|
2
|
+
import Component from "sap/ui/core/Component";
|
|
3
|
+
import AppComponent from "sap/fe/core/AppComponent";
|
|
4
|
+
|
|
5
|
+
export type User = {
|
|
6
|
+
id: string;
|
|
7
|
+
initials?: string;
|
|
8
|
+
name: string;
|
|
9
|
+
role?: Role;
|
|
10
|
+
color?: number;
|
|
11
|
+
transient?: boolean;
|
|
12
|
+
me?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type UserActivity = User & {
|
|
16
|
+
key?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export enum Role {
|
|
20
|
+
Owner = "O",
|
|
21
|
+
Edit = "E"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export enum Activity {
|
|
25
|
+
Join = "JOIN",
|
|
26
|
+
JoinEcho = "JOINECHO",
|
|
27
|
+
Leave = "LEAVE",
|
|
28
|
+
Change = "CHANGE",
|
|
29
|
+
Create = "CREATE",
|
|
30
|
+
Delete = "DELETE",
|
|
31
|
+
Action = "ACTION",
|
|
32
|
+
LiveChange = "LIVECHANGE",
|
|
33
|
+
Activate = "ACTIVATE",
|
|
34
|
+
Discard = "DISCARD",
|
|
35
|
+
Undo = "UNDO"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type Message = {
|
|
39
|
+
userDescription: string;
|
|
40
|
+
userID: string;
|
|
41
|
+
userAction: string;
|
|
42
|
+
clientAction: string;
|
|
43
|
+
clientContent: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function formatInitials(fullName: string): string {
|
|
47
|
+
// remove titles - those are the ones from S/4 to be checked if there are others
|
|
48
|
+
const academicTitles = ["Dr.", "Prof.", "Prof. Dr.", "B.A.", "MBA", "Ph.D."];
|
|
49
|
+
academicTitles.forEach(function(academicTitle) {
|
|
50
|
+
fullName = fullName.replace(academicTitle, "");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let initials: string;
|
|
54
|
+
const parts = fullName.trimStart().split(" ");
|
|
55
|
+
|
|
56
|
+
if (parts.length > 1) {
|
|
57
|
+
initials = (parts?.shift()?.charAt(0) || "") + parts.pop()?.charAt(0);
|
|
58
|
+
} else {
|
|
59
|
+
initials = fullName.substring(0, 2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return initials.toUpperCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getUserColor(UserID: string, activeUsers: User[], invitedUsers: User[]) {
|
|
66
|
+
// search if user is known
|
|
67
|
+
const user = activeUsers.find(u => u.id === UserID);
|
|
68
|
+
if (user) {
|
|
69
|
+
return user.color;
|
|
70
|
+
} else {
|
|
71
|
+
// search for next free color
|
|
72
|
+
for (let i = 1; i <= 10; i++) {
|
|
73
|
+
if (activeUsers.findIndex(u => u.color === i) === -1 && invitedUsers.findIndex(u => u.color === i) === -1) {
|
|
74
|
+
return i;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// this seems to be a popular object :) for now just return 10 for all.
|
|
78
|
+
// for invited we should start from 1 again so the colors are different
|
|
79
|
+
return 10;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// copied from CommonUtils. Due to a cycle dependency I can't use CommonUtils here.
|
|
84
|
+
// That's to be fixed. the discard popover thingy shouldn't be in the common utils at all
|
|
85
|
+
function getAppComponent(oControl: any): AppComponent {
|
|
86
|
+
if (oControl.isA("sap.fe.core.AppComponent")) {
|
|
87
|
+
return oControl;
|
|
88
|
+
}
|
|
89
|
+
const oOwner = Component.getOwnerComponentFor(oControl);
|
|
90
|
+
if (!oOwner) {
|
|
91
|
+
return oControl;
|
|
92
|
+
} else {
|
|
93
|
+
return getAppComponent(oOwner);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getMe(view: View): User {
|
|
98
|
+
const shellServiceHelper = getAppComponent(view).getShellServices();
|
|
99
|
+
if (!shellServiceHelper || !shellServiceHelper.hasUShell()) {
|
|
100
|
+
throw "No Shell... No User";
|
|
101
|
+
}
|
|
102
|
+
const me: User = {
|
|
103
|
+
initials: shellServiceHelper.getUser().getInitials(),
|
|
104
|
+
id: shellServiceHelper.getUser().getId(),
|
|
105
|
+
name: shellServiceHelper.getUser().getFullName(),
|
|
106
|
+
color: 6, // same color as FLP...
|
|
107
|
+
role: Role.Owner,
|
|
108
|
+
me: true
|
|
109
|
+
};
|
|
110
|
+
me.name += " (You)"; // TODO resource bundle
|
|
111
|
+
return me;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const CollaborationUtils = {
|
|
115
|
+
formatInitials: formatInitials,
|
|
116
|
+
getUserColor: getUserColor,
|
|
117
|
+
getMe: getMe,
|
|
118
|
+
getAppComponent: getAppComponent
|
|
119
|
+
};
|