Improve layout of share details page
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Fri, 18 Nov 2022 16:42:58 +0000 (17:42 +0100)
committerClaudio Cambra <claudio.cambra@gmail.com>
Fri, 9 Dec 2022 11:54:09 +0000 (12:54 +0100)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
src/gui/filedetails/ShareDetailsPage.qml

index 75ef88df6befb0e86fb0869b05242865d208f755..1376d3ca252ddaf24ee6cf043e43b8f823265caa 100644 (file)
@@ -180,7 +180,7 @@ Page {
     }
 
     header: ColumnLayout {
-        spacing: root.intendedPadding
+        spacing: root.padding
 
         GridLayout {
             id: headerGridLayout
@@ -250,488 +250,505 @@ Page {
         }
     }
 
-    ColumnLayout {
-        id: moreMenu
+    contentItem: ColumnLayout {
+        ScrollView {
+            Layout.fillWidth: true
+            Layout.fillHeight: true
 
-        property int rowIconWidth: 16
-        property int indicatorItemWidth: 20
-        property int indicatorSpacing: Style.standardSpacing
-        property int itemPadding: Style.smallSpacing
+            contentWidth: availableWidth
 
-        RowLayout {
-            anchors.left: parent.left
-            anchors.leftMargin: moreMenu.itemPadding
-            anchors.right: parent.right
-            anchors.rightMargin: moreMenu.itemPadding
-            height: visible ? implicitHeight : 0
-            spacing: moreMenu.indicatorSpacing
+            clip: true
 
-            visible: root.isLinkShare
+            ColumnLayout {
+                id: moreMenu
 
-            Image {
-                Layout.preferredWidth: moreMenu.indicatorItemWidth
-                Layout.fillHeight: true
+                property int rowIconWidth: 16
+                property int indicatorItemWidth: 20
+                property int indicatorSpacing: Style.standardSpacing
+                property int itemPadding: Style.smallSpacing
 
-                verticalAlignment: Image.AlignVCenter
-                horizontalAlignment: Image.AlignHCenter
-                fillMode: Image.Pad
+                width: parent.width
 
-                source: "image://svgimage-custom-color/edit.svg/" + Style.menuBorder
-                sourceSize.width: moreMenu.rowIconWidth
-                sourceSize.height: moreMenu.rowIconWidth
-            }
+                RowLayout {
+                    Layout.fillWidth: true
+                    height: visible ? implicitHeight : 0
+                    spacing: moreMenu.indicatorSpacing
 
-            NCInputTextField {
-                id: linkShareLabelTextField
+                    visible: root.isLinkShare
 
-                Layout.fillWidth: true
-                height: visible ? implicitHeight : 0
+                    Image {
+                        Layout.preferredWidth: moreMenu.indicatorItemWidth
+                        Layout.fillHeight: true
 
-                text: root.linkShareLabel
-                placeholderText: qsTr("Share label")
+                        verticalAlignment: Image.AlignVCenter
+                        horizontalAlignment: Image.AlignHCenter
+                        fillMode: Image.Pad
 
-                enabled: root.isLinkShare &&
-                         !root.waitingForLinkShareLabelChange
-
-                onAccepted: if(text !== root.linkShareLabel) {
-                    root.setLinkShareLabel(text);
-                    root.waitingForLinkShareLabelChange = true;
-                }
-
-                NCBusyIndicator {
-                    anchors.fill: parent
-                    visible: root.waitingForLinkShareLabelChange
-                    running: visible
-                    z: 1
-                }
-            }
-        }
-
-        // On these checkables, the clicked() signal is called after
-        // the check state changes.
-        CheckBox {
-            id: editingAllowedMenuItem
-
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-            indicator.width: moreMenu.indicatorItemWidth
-            indicator.height: moreMenu.indicatorItemWidth
+                        source: "image://svgimage-custom-color/edit.svg/" + Style.menuBorder
+                        sourceSize.width: moreMenu.rowIconWidth
+                        sourceSize.height: moreMenu.rowIconWidth
+                    }
 
-            checkable: true
-            checked: root.editingAllowed
-            text: qsTr("Allow editing")
-            enabled: !root.waitingForEditingAllowedChange
+                    NCInputTextField {
+                        id: linkShareLabelTextField
 
-            onClicked: {
-                root.toggleAllowEditing(checked);
-                root.waitingForEditingAllowedChange = true;
-            }
+                        Layout.fillWidth: true
+                        height: visible ? implicitHeight : 0
 
-            NCBusyIndicator {
-                anchors.fill: parent
-                visible: root.waitingForEditingAllowedChange
-                running: visible
-                z: 1
-            }
-        }
+                        text: root.linkShareLabel
+                        placeholderText: qsTr("Share label")
 
-        CheckBox {
-            id: passwordProtectEnabledMenuItem
+                        enabled: root.isLinkShare &&
+                                 !root.waitingForLinkShareLabelChange
 
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-            indicator.width: moreMenu.indicatorItemWidth
-            indicator.height: moreMenu.indicatorItemWidth
+                        onAccepted: if(text !== root.linkShareLabel) {
+                            root.setLinkShareLabel(text);
+                            root.waitingForLinkShareLabelChange = true;
+                        }
 
-            checkable: true
-            checked: root.passwordProtectEnabled
-            text: qsTr("Password protect")
-            enabled: !root.waitingForPasswordProtectEnabledChange && !root.passwordEnforced
+                        NCBusyIndicator {
+                            anchors.fill: parent
+                            visible: root.waitingForLinkShareLabelChange
+                            running: visible
+                            z: 1
+                        }
+                    }
+                }
 
-            onClicked: {
-                root.togglePasswordProtect(checked);
-                root.waitingForPasswordProtectEnabledChange = true;
-            }
+                // On these checkables, the clicked() signal is called after
+                // the check state changes.
+                CheckBox {
+                    id: editingAllowedMenuItem
 
-            NCBusyIndicator {
-                anchors.fill: parent
-                visible: root.waitingForPasswordProtectEnabledChange
-                running: visible
-                z: 1
-            }
-        }
+                    Layout.fillWidth: true
 
-        RowLayout {
-            anchors.left: parent.left
-            anchors.leftMargin: moreMenu.itemPadding
-            anchors.right: parent.right
-            anchors.rightMargin: moreMenu.itemPadding
-            height: visible ? implicitHeight : 0
-            spacing: moreMenu.indicatorSpacing
+                    spacing: moreMenu.indicatorSpacing
+                    padding: moreMenu.itemPadding
+                    indicator.width: moreMenu.indicatorItemWidth
+                    indicator.height: moreMenu.indicatorItemWidth
 
-            visible: root.passwordProtectEnabled
+                    checkable: true
+                    checked: root.editingAllowed
+                    text: qsTr("Allow editing")
+                    enabled: !root.waitingForEditingAllowedChange
 
-            Image {
-                Layout.preferredWidth: moreMenu.indicatorItemWidth
-                Layout.fillHeight: true
+                    onClicked: {
+                        root.toggleAllowEditing(checked);
+                        root.waitingForEditingAllowedChange = true;
+                    }
 
-                verticalAlignment: Image.AlignVCenter
-                horizontalAlignment: Image.AlignHCenter
-                fillMode: Image.Pad
+                    NCBusyIndicator {
+                        anchors.fill: parent
+                        visible: root.waitingForEditingAllowedChange
+                        running: visible
+                        z: 1
+                    }
+                }
 
-                source: "image://svgimage-custom-color/lock-https.svg/" + Style.menuBorder
-                sourceSize.width: moreMenu.rowIconWidth
-                sourceSize.height: moreMenu.rowIconWidth
-            }
+                CheckBox {
+                    id: passwordProtectEnabledMenuItem
 
-            NCInputTextField {
-                id: passwordTextField
+                    Layout.fillWidth: true
 
-                Layout.fillWidth: true
-                height: visible ? implicitHeight : 0
+                    spacing: moreMenu.indicatorSpacing
+                    padding: moreMenu.itemPadding
+                    indicator.width: moreMenu.indicatorItemWidth
+                    indicator.height: moreMenu.indicatorItemWidth
 
-                text: root.password !== "" ? root.password : root.passwordPlaceholder
-                enabled: root.passwordProtectEnabled &&
-                         !root.waitingForPasswordChange &&
-                         !root.waitingForPasswordProtectEnabledChange
+                    checkable: true
+                    checked: root.passwordProtectEnabled
+                    text: qsTr("Password protect")
+                    enabled: !root.waitingForPasswordProtectEnabledChange && !root.passwordEnforced
 
-                onAccepted: if(text !== root.password && text !== root.passwordPlaceholder) {
-                    passwordErrorBoxLoader.message = "";
-                    root.setPassword(text);
-                    root.waitingForPasswordChange = true;
-                }
+                    onClicked: {
+                        root.togglePasswordProtect(checked);
+                        root.waitingForPasswordProtectEnabledChange = true;
+                    }
 
-                NCBusyIndicator {
-                    anchors.fill: parent
-                    visible: root.waitingForPasswordChange ||
-                             root.waitingForPasswordProtectEnabledChange
-                    running: visible
-                    z: 1
+                    NCBusyIndicator {
+                        anchors.fill: parent
+                        visible: root.waitingForPasswordProtectEnabledChange
+                        running: visible
+                        z: 1
+                    }
                 }
-            }
-        }
 
-        Loader {
-            id: passwordErrorBoxLoader
+                RowLayout {
+                    Layout.fillWidth: true
 
-            property string message: ""
+                    height: visible ? implicitHeight : 0
+                    spacing: moreMenu.indicatorSpacing
 
-            anchors.left: parent.left
-            anchors.right: parent.right
-            height: message !== "" ? implicitHeight : 0
+                    visible: root.passwordProtectEnabled
 
-            active: message !== ""
-            visible: active
+                    Image {
+                        Layout.preferredWidth: moreMenu.indicatorItemWidth
+                        Layout.fillHeight: true
 
-            sourceComponent: Item {
-                anchors.top: parent.top
-                anchors.left: parent.left
-                anchors.right: parent.right
-                // Artificially add vertical padding
-                implicitHeight: passwordErrorBox.implicitHeight + (Style.smallSpacing * 2)
+                        verticalAlignment: Image.AlignVCenter
+                        horizontalAlignment: Image.AlignHCenter
+                        fillMode: Image.Pad
 
-                ErrorBox {
-                    id: passwordErrorBox
-                    anchors.left: parent.left
-                    anchors.right: parent.right
-                    anchors.verticalCenter: parent.verticalCenter
+                        source: "image://svgimage-custom-color/lock-https.svg/" + Style.menuBorder
+                        sourceSize.width: moreMenu.rowIconWidth
+                        sourceSize.height: moreMenu.rowIconWidth
+                    }
 
-                    text: passwordErrorBoxLoader.message
+                    NCInputTextField {
+                        id: passwordTextField
+
+                        Layout.fillWidth: true
+                        height: visible ? implicitHeight : 0
+
+                        text: root.password !== "" ? root.password : root.passwordPlaceholder
+                        enabled: root.passwordProtectEnabled &&
+                                 !root.waitingForPasswordChange &&
+                                 !root.waitingForPasswordProtectEnabledChange
+
+                        onAccepted: if(text !== root.password && text !== root.passwordPlaceholder) {
+                            passwordErrorBoxLoader.message = "";
+                            root.setPassword(text);
+                            root.waitingForPasswordChange = true;
+                        }
+
+                        NCBusyIndicator {
+                            anchors.fill: parent
+                            visible: root.waitingForPasswordChange ||
+                                     root.waitingForPasswordProtectEnabledChange
+                            running: visible
+                            z: 1
+                        }
+                    }
                 }
-            }
-        }
 
-        CheckBox {
-            id: expireDateEnabledMenuItem
+                Loader {
+                    id: passwordErrorBoxLoader
 
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-            indicator.width: moreMenu.indicatorItemWidth
-            indicator.height: moreMenu.indicatorItemWidth
+                    property string message: ""
 
-            checkable: true
-            checked: root.expireDateEnabled
-            text: qsTr("Set expiration date")
-            enabled: !root.waitingForExpireDateEnabledChange && !root.expireDateEnforced
+                    Layout.fillWidth: true
+                    height: message !== "" ? implicitHeight : 0
 
-            onClicked: {
-                root.toggleExpirationDate(checked);
-                root.waitingForExpireDateEnabledChange = true;
-            }
+                    active: message !== ""
+                    visible: active
 
-            NCBusyIndicator {
-                anchors.fill: parent
-                visible: root.waitingForExpireDateEnabledChange
-                running: visible
-                z: 1
-            }
-        }
+                    sourceComponent: Item {
+                        anchors.top: parent.top
+                        anchors.left: parent.left
+                        anchors.right: parent.right
+                        // Artificially add vertical padding
+                        implicitHeight: passwordErrorBox.implicitHeight + (Style.smallSpacing * 2)
 
-        RowLayout {
-            anchors.left: parent.left
-            anchors.leftMargin: moreMenu.itemPadding
-            anchors.right: parent.right
-            anchors.rightMargin: moreMenu.itemPadding
-            height: visible ? implicitHeight : 0
-            spacing: moreMenu.indicatorSpacing
+                        ErrorBox {
+                            id: passwordErrorBox
+                            anchors.left: parent.left
+                            anchors.right: parent.right
+                            anchors.verticalCenter: parent.verticalCenter
 
-            visible: root.expireDateEnabled
+                            text: passwordErrorBoxLoader.message
+                        }
+                    }
+                }
 
-            Image {
-                Layout.preferredWidth: moreMenu.indicatorItemWidth
-                Layout.fillHeight: true
+                CheckBox {
+                    id: expireDateEnabledMenuItem
 
-                verticalAlignment: Image.AlignVCenter
-                horizontalAlignment: Image.AlignHCenter
-                fillMode: Image.Pad
+                    Layout.fillWidth: true
 
-                source: "image://svgimage-custom-color/calendar.svg/" + Style.menuBorder
-                sourceSize.width: moreMenu.rowIconWidth
-                sourceSize.height: moreMenu.rowIconWidth
-            }
+                    spacing: moreMenu.indicatorSpacing
+                    padding: moreMenu.itemPadding
+                    indicator.width: moreMenu.indicatorItemWidth
+                    indicator.height: moreMenu.indicatorItemWidth
 
-            // QML dates are essentially JavaScript dates, which makes them very finicky and unreliable.
-            // Instead, we exclusively deal with msecs from epoch time to make things less painful when editing.
-            // We only use the QML Date when showing the nice string to the user.
-            SpinBox {
-                id: expireDateSpinBox
-
-                // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch
-                // Instead, we handle everything as days since epoch
-                readonly property int dayInMSecs: 24 * 60 * 60 * 1000
-                readonly property int expireDateReduced: Math.floor(root.expireDate / dayInMSecs)
-                // Reset the model data after binding broken on user interact
-                onExpireDateReducedChanged: value = expireDateReduced
-
-                // We can't use JS's convenient Infinity or Number.MAX_VALUE as
-                // JS Number type is 64 bits, whereas QML's int type is only 32 bits
-                readonly property IntValidator intValidator: IntValidator {}
-                readonly property int maximumExpireDateReduced: root.expireDateEnforced ?
-                                                                    Math.floor(root.maximumExpireDate / dayInMSecs) :
-                                                                    intValidator.top
-                readonly property int minimumExpireDateReduced: {
-                    const currentDate = new Date();
-                    const minDateUTC = new Date(Date.UTC(currentDate.getFullYear(),
-                                                         currentDate.getMonth(),
-                                                         currentDate.getDate() + 1));
-                    return Math.floor(minDateUTC / dayInMSecs) // Start of day at 00:00:0000 UTC
-                }
+                    checkable: true
+                    checked: root.expireDateEnabled
+                    text: qsTr("Set expiration date")
+                    enabled: !root.waitingForExpireDateEnabledChange && !root.expireDateEnforced
 
-                // Taken from Kalendar 22.08
-                // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js
-                function parseDateString(dateString) {
-                    function defaultParse() {
-                        const defaultParsedDate = Date.fromLocaleDateString(Qt.locale(), dateString, Locale.NarrowFormat);
-                        // JS always generates date in system locale, eliminate timezone difference to UTC
-                        const msecsSinceEpoch = defaultParsedDate.getTime() - (defaultParsedDate.getTimezoneOffset() * 60 * 1000);
-                        return new Date(msecsSinceEpoch);
+                    onClicked: {
+                        root.toggleExpirationDate(checked);
+                        root.waitingForExpireDateEnabledChange = true;
                     }
 
-                    const dateStringDelimiterMatches = dateString.match(/\D/);
-                    if(dateStringDelimiterMatches.length === 0) {
-                        // Let the date method figure out this weirdness
-                        return defaultParse();
+                    NCBusyIndicator {
+                        anchors.fill: parent
+                        visible: root.waitingForExpireDateEnabledChange
+                        running: visible
+                        z: 1
                     }
+                }
 
-                    const dateStringDelimiter = dateStringDelimiterMatches[0];
-
-                    const localisedDateFormatSplit = Qt.locale().dateFormat(Locale.NarrowFormat).split(dateStringDelimiter);
-                    const localisedDateDayPosition = localisedDateFormatSplit.findIndex((x) => /d/gi.test(x));
-                    const localisedDateMonthPosition = localisedDateFormatSplit.findIndex((x) => /m/gi.test(x));
-                    const localisedDateYearPosition = localisedDateFormatSplit.findIndex((x) => /y/gi.test(x));
+                RowLayout {
+                    Layout.fillWidth: true
+                    height: visible ? implicitHeight : 0
+                    spacing: moreMenu.indicatorSpacing
 
-                    let splitDateString = dateString.split(dateStringDelimiter);
-                    let userProvidedYear = splitDateString[localisedDateYearPosition]
+                    visible: root.expireDateEnabled
 
-                    const dateNow = new Date();
-                    const stringifiedCurrentYear = dateNow.getFullYear().toString();
+                    Image {
+                        Layout.preferredWidth: moreMenu.indicatorItemWidth
+                        Layout.fillHeight: true
 
-                    // If we have any input weirdness, or if we have a fully-written year
-                    // (e.g. 2022 instead of 22) then use default parse
-                    if(splitDateString.length === 0 ||
-                            splitDateString.length > 3 ||
-                            userProvidedYear.length >= stringifiedCurrentYear.length) {
+                        verticalAlignment: Image.AlignVCenter
+                        horizontalAlignment: Image.AlignHCenter
+                        fillMode: Image.Pad
 
-                        return defaultParse();
+                        source: "image://svgimage-custom-color/calendar.svg/" + Style.menuBorder
+                        sourceSize.width: moreMenu.rowIconWidth
+                        sourceSize.height: moreMenu.rowIconWidth
                     }
 
-                    let fullyWrittenYear = userProvidedYear.split("");
-                    const digitsToAdd = stringifiedCurrentYear.length - fullyWrittenYear.length;
-                    for(let i = 0; i < digitsToAdd; i++) {
-                        fullyWrittenYear.splice(i, 0, stringifiedCurrentYear[i])
+                    // QML dates are essentially JavaScript dates, which makes them very finicky and unreliable.
+                    // Instead, we exclusively deal with msecs from epoch time to make things less painful when editing.
+                    // We only use the QML Date when showing the nice string to the user.
+                    SpinBox {
+                        id: expireDateSpinBox
+
+                        // Work arounds the limitations of QML's 32 bit integer when handling msecs from epoch
+                        // Instead, we handle everything as days since epoch
+                        readonly property int dayInMSecs: 24 * 60 * 60 * 1000
+                        readonly property int expireDateReduced: Math.floor(root.expireDate / dayInMSecs)
+                        // Reset the model data after binding broken on user interact
+                        onExpireDateReducedChanged: value = expireDateReduced
+
+                        // We can't use JS's convenient Infinity or Number.MAX_VALUE as
+                        // JS Number type is 64 bits, whereas QML's int type is only 32 bits
+                        readonly property IntValidator intValidator: IntValidator {}
+                        readonly property int maximumExpireDateReduced: root.expireDateEnforced ?
+                                                                            Math.floor(root.maximumExpireDate / dayInMSecs) :
+                                                                            intValidator.top
+                        readonly property int minimumExpireDateReduced: {
+                            const currentDate = new Date();
+                            const minDateUTC = new Date(Date.UTC(currentDate.getFullYear(),
+                                                                 currentDate.getMonth(),
+                                                                 currentDate.getDate() + 1));
+                            return Math.floor(minDateUTC / dayInMSecs) // Start of day at 00:00:0000 UTC
+                        }
+
+                        // Taken from Kalendar 22.08
+                        // https://invent.kde.org/pim/kalendar/-/blob/release/22.08/src/contents/ui/KalendarUtils/dateutils.js
+                        function parseDateString(dateString) {
+                            function defaultParse() {
+                                const defaultParsedDate = Date.fromLocaleDateString(Qt.locale(), dateString, Locale.NarrowFormat);
+                                // JS always generates date in system locale, eliminate timezone difference to UTC
+                                const msecsSinceEpoch = defaultParsedDate.getTime() - (defaultParsedDate.getTimezoneOffset() * 60 * 1000);
+                                return new Date(msecsSinceEpoch);
+                            }
+
+                            const dateStringDelimiterMatches = dateString.match(/\D/);
+                            if(dateStringDelimiterMatches.length === 0) {
+                                // Let the date method figure out this weirdness
+                                return defaultParse();
+                            }
+
+                            const dateStringDelimiter = dateStringDelimiterMatches[0];
+
+                            const localisedDateFormatSplit = Qt.locale().dateFormat(Locale.NarrowFormat).split(dateStringDelimiter);
+                            const localisedDateDayPosition = localisedDateFormatSplit.findIndex((x) => /d/gi.test(x));
+                            const localisedDateMonthPosition = localisedDateFormatSplit.findIndex((x) => /m/gi.test(x));
+                            const localisedDateYearPosition = localisedDateFormatSplit.findIndex((x) => /y/gi.test(x));
+
+                            let splitDateString = dateString.split(dateStringDelimiter);
+                            let userProvidedYear = splitDateString[localisedDateYearPosition]
+
+                            const dateNow = new Date();
+                            const stringifiedCurrentYear = dateNow.getFullYear().toString();
+
+                            // If we have any input weirdness, or if we have a fully-written year
+                            // (e.g. 2022 instead of 22) then use default parse
+                            if(splitDateString.length === 0 ||
+                                    splitDateString.length > 3 ||
+                                    userProvidedYear.length >= stringifiedCurrentYear.length) {
+
+                                return defaultParse();
+                            }
+
+                            let fullyWrittenYear = userProvidedYear.split("");
+                            const digitsToAdd = stringifiedCurrentYear.length - fullyWrittenYear.length;
+                            for(let i = 0; i < digitsToAdd; i++) {
+                                fullyWrittenYear.splice(i, 0, stringifiedCurrentYear[i])
+                            }
+                            fullyWrittenYear = fullyWrittenYear.join("");
+
+                            const fixedYearNum = Number(fullyWrittenYear);
+                            const monthIndexNum = Number(splitDateString[localisedDateMonthPosition]) - 1;
+                            const dayNum = Number(splitDateString[localisedDateDayPosition]);
+
+                            console.log(dayNum, monthIndexNum, fixedYearNum);
+
+                            // Modification: return date in UTC
+                            return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum));
+                        }
+
+                        Layout.fillWidth: true
+                        height: visible ? implicitHeight : 0
+
+
+                        // We want all the internal benefits of the spinbox but don't actually want the
+                        // buttons, so set an empty item as a dummy
+                        up.indicator: Item {}
+                        down.indicator: Item {}
+
+                        background: Rectangle {
+                            radius: Style.slightlyRoundedButtonRadius
+                            border.width: Style.normalBorderWidth
+                            border.color: expireDateSpinBox.activeFocus ? Style.ncBlue : Style.menuBorder
+                            color: Style.backgroundColor
+                        }
+
+                        value: expireDateReduced
+                        from: minimumExpireDateReduced
+                        to: maximumExpireDateReduced
+
+                        textFromValue: (value, locale) => {
+                            const dateFromValue = new Date(value * dayInMSecs);
+                            return dateFromValue.toLocaleDateString(Qt.locale(), Locale.NarrowFormat);
+                        }
+                        valueFromText: (text, locale) => {
+                            const dateFromText = parseDateString(text);
+                            return Math.floor(dateFromText.getTime() / dayInMSecs);
+                        }
+
+                        editable: true
+                        inputMethodHints: Qt.ImhDate | Qt.ImhFormattedNumbersOnly
+
+                        enabled: root.expireDateEnabled &&
+                                 !root.waitingForExpireDateChange &&
+                                 !root.waitingForExpireDateEnabledChange
+
+                        onValueModified: {
+                            if (!enabled || !activeFocus) {
+                                return;
+                            }
+
+                            root.setExpireDate(value * dayInMSecs);
+                            root.waitingForExpireDateChange = true;
+                        }
+
+                        NCBusyIndicator {
+                            anchors.fill: parent
+                            visible: root.waitingForExpireDateEnabledChange ||
+                                     root.waitingForExpireDateChange
+                            running: visible
+                            z: 1
+                        }
                     }
-                    fullyWrittenYear = fullyWrittenYear.join("");
-
-                    const fixedYearNum = Number(fullyWrittenYear);
-                    const monthIndexNum = Number(splitDateString[localisedDateMonthPosition]) - 1;
-                    const dayNum = Number(splitDateString[localisedDateDayPosition]);
-
-                    console.log(dayNum, monthIndexNum, fixedYearNum);
-
-                    // Modification: return date in UTC
-                    return new Date(Date.UTC(fixedYearNum, monthIndexNum, dayNum));
                 }
 
-                Layout.fillWidth: true
-                height: visible ? implicitHeight : 0
-
+                CheckBox {
+                    id: noteEnabledMenuItem
 
-                // We want all the internal benefits of the spinbox but don't actually want the
-                // buttons, so set an empty item as a dummy
-                up.indicator: Item {}
-                down.indicator: Item {}
+                    Layout.fillWidth: true
 
-                background: Rectangle {
-                    radius: Style.slightlyRoundedButtonRadius
-                    border.width: Style.normalBorderWidth
-                    border.color: expireDateSpinBox.activeFocus ? Style.ncBlue : Style.menuBorder
-                    color: Style.backgroundColor
-                }
+                    spacing: moreMenu.indicatorSpacing
+                    padding: moreMenu.itemPadding
+                    indicator.width: moreMenu.indicatorItemWidth
+                    indicator.height: moreMenu.indicatorItemWidth
 
-                value: expireDateReduced
-                from: minimumExpireDateReduced
-                to: maximumExpireDateReduced
+                    checkable: true
+                    checked: root.noteEnabled
+                    text: qsTr("Note to recipient")
+                    enabled: !root.waitingForNoteEnabledChange
 
-                textFromValue: (value, locale) => {
-                    const dateFromValue = new Date(value * dayInMSecs);
-                    return dateFromValue.toLocaleDateString(Qt.locale(), Locale.NarrowFormat);
-                }
-                valueFromText: (text, locale) => {
-                    const dateFromText = parseDateString(text);
-                    return Math.floor(dateFromText.getTime() / dayInMSecs);
-                }
-
-                editable: true
-                inputMethodHints: Qt.ImhDate | Qt.ImhFormattedNumbersOnly
-
-                enabled: root.expireDateEnabled &&
-                         !root.waitingForExpireDateChange &&
-                         !root.waitingForExpireDateEnabledChange
-
-                onValueModified: {
-                    if (!enabled || !activeFocus) {
-                        return;
+                    onClicked: {
+                        root.toggleNoteToRecipient(checked);
+                        root.waitingForNoteEnabledChange = true;
                     }
 
-                    root.setExpireDate(value * dayInMSecs);
-                    root.waitingForExpireDateChange = true;
+                    NCBusyIndicator {
+                        anchors.fill: parent
+                        visible: root.waitingForNoteEnabledChange
+                        running: visible
+                        z: 1
+                    }
                 }
 
-                NCBusyIndicator {
-                    anchors.fill: parent
-                    visible: root.waitingForExpireDateEnabledChange ||
-                             root.waitingForExpireDateChange
-                    running: visible
-                    z: 1
-                }
-            }
-        }
+                RowLayout {
+                    Layout.fillWidth: true
+                    height: visible ? implicitHeight : 0
+                    spacing: moreMenu.indicatorSpacing
 
-        CheckBox {
-            id: noteEnabledMenuItem
+                    visible: root.noteEnabled
 
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-            indicator.width: moreMenu.indicatorItemWidth
-            indicator.height: moreMenu.indicatorItemWidth
+                    Image {
+                        Layout.preferredWidth: moreMenu.indicatorItemWidth
+                        Layout.fillHeight: true
 
-            checkable: true
-            checked: root.noteEnabled
-            text: qsTr("Note to recipient")
-            enabled: !root.waitingForNoteEnabledChange
+                        verticalAlignment: Image.AlignVCenter
+                        horizontalAlignment: Image.AlignHCenter
+                        fillMode: Image.Pad
 
-            onClicked: {
-                root.toggleNoteToRecipient(checked);
-                root.waitingForNoteEnabledChange = true;
-            }
+                        source: "image://svgimage-custom-color/edit.svg/" + Style.menuBorder
+                        sourceSize.width: moreMenu.rowIconWidth
+                        sourceSize.height: moreMenu.rowIconWidth
+                    }
 
-            NCBusyIndicator {
-                anchors.fill: parent
-                visible: root.waitingForNoteEnabledChange
-                running: visible
-                z: 1
+                    NCInputTextEdit {
+                        id: noteTextEdit
+
+                        Layout.fillWidth: true
+                        height: visible ? Math.max(Style.talkReplyTextFieldPreferredHeight, contentHeight) : 0
+                        submitButton.height: Math.min(Style.talkReplyTextFieldPreferredHeight, height - 2)
+
+                        text: root.note
+                        enabled: root.noteEnabled &&
+                                 !root.waitingForNoteChange &&
+                                 !root.waitingForNoteEnabledChange
+
+                        onEditingFinished: if(text !== root.note) {
+                            root.setNote(text);
+                            root.waitingForNoteChange = true;
+                        }
+
+                        NCBusyIndicator {
+                            anchors.fill: parent
+                            visible: root.waitingForNoteChange ||
+                                     root.waitingForNoteEnabledChange
+                            running: visible
+                            z: 1
+                        }
+                    }
+                }
             }
         }
 
         RowLayout {
-            anchors.left: parent.left
-            anchors.leftMargin: moreMenu.itemPadding
-            anchors.right: parent.right
-            anchors.rightMargin: moreMenu.itemPadding
-            height: visible ? implicitHeight : 0
-            spacing: moreMenu.indicatorSpacing
-
-            visible: root.noteEnabled
-
-            Image {
-                Layout.preferredWidth: moreMenu.indicatorItemWidth
-                Layout.fillHeight: true
-
-                verticalAlignment: Image.AlignVCenter
-                horizontalAlignment: Image.AlignHCenter
-                fillMode: Image.Pad
+            Layout.fillWidth: true
 
-                source: "image://svgimage-custom-color/edit.svg/" + Style.menuBorder
-                sourceSize.width: moreMenu.rowIconWidth
-                sourceSize.height: moreMenu.rowIconWidth
+            CustomButton {
+                Layout.fillWidth: true
+                implicitWidth: parent.width / 2
+                height: Style.standardPrimaryButtonHeight
+
+                imageSource: "image://svgimage-custom-color/close.svg/" + Style.ncHeaderTextColor
+                text: qsTr("Unshare")
+                textColor: Style.ncHeaderTextColor
+                contentsFont.bold: true
+                bgColor: Style.errorBoxBackgroundColor
+                bgNormalOpacity: 1.0
+                bgHoverOpacity: Style.hoverOpacity
+
+                onClicked: root.deleteShare()
             }
 
-            NCInputTextEdit {
-                id: noteTextEdit
-
+            CustomButton {
                 Layout.fillWidth: true
-                height: visible ? Math.max(Style.talkReplyTextFieldPreferredHeight, contentHeight) : 0
-                submitButton.height: Math.min(Style.talkReplyTextFieldPreferredHeight, height - 2)
+                implicitWidth: parent.width / 2
+                height: Style.standardPrimaryButtonHeight
 
-                text: root.note
-                enabled: root.noteEnabled &&
-                         !root.waitingForNoteChange &&
-                         !root.waitingForNoteEnabledChange
+                imageSource: "image://svgimage-custom-color/add.svg/" + Style.ncHeaderTextColor
+                text: qsTr("Add another link")
+                textColor: Style.ncHeaderTextColor
+                contentsFont.bold: true
+                bgColor: Style.ncBlue
+                bgNormalOpacity: 1.0
+                bgHoverOpacity: Style.hoverOpacity
 
-                onEditingFinished: if(text !== root.note) {
-                    root.setNote(text);
-                    root.waitingForNoteChange = true;
-                }
+                visible: root.isLinkShare && root.canCreateLinkShares
+                enabled: visible
 
-                NCBusyIndicator {
-                    anchors.fill: parent
-                    visible: root.waitingForNoteChange ||
-                             root.waitingForNoteEnabledChange
-                    running: visible
-                    z: 1
-                }
+                onClicked: root.createNewLinkShare()
             }
         }
-
-        MenuItem {
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-
-            icon.width: moreMenu.indicatorItemWidth
-            icon.height: moreMenu.indicatorItemWidth
-            icon.color: Style.ncTextColor
-            icon.source: "qrc:///client/theme/close.svg"
-            text: qsTr("Unshare")
-
-            onTriggered: root.deleteShare()
-        }
-
-        MenuItem {
-            height: visible ? implicitHeight : 0
-            spacing: moreMenu.indicatorSpacing
-            padding: moreMenu.itemPadding
-
-            icon.width: moreMenu.indicatorItemWidth
-            icon.height: moreMenu.indicatorItemWidth
-            icon.color: Style.ncTextColor
-            icon.source: "qrc:///client/theme/add.svg"
-            text: qsTr("Add another link")
-
-            visible: root.isLinkShare && root.canCreateLinkShares
-            enabled: visible
-
-            onTriggered: root.createNewLinkShare()
-        }
     }
-
 }