diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9040a7d..2acbb835d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,18 @@ All notable changes to this project will be documented in this file. -## [0.27.31] +## [0.27.36] ### Bug Fixes +- 打开会话面板报错 - 子任务通知无法打开 ### Performance +- 优化会员选择器 +- 优化会员选择器 +- 优化图片压缩 - 回复图片显示图片搜略图 - 优化会员选择器 - 会员选择下拉框提示 diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index bb4242856..2889500b9 100755 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -56,9 +56,15 @@ class ProjectController extends AbstractController * - all:全部 * - no:未归档(默认) * - yes:已归档 - * @apiParam {String} [getcolumn] 同时取项目列表 + * @apiParam {String} [getcolumn] 同时取列表 * - no:不取(默认) * - yes:取列表 + * @apiParam {String} [getuserid] 同时取成员ID + * - no:不取(默认) + * - yes:取列表 + * @apiParam {String} [getstatistics] 同时取任务统计 + * - no:不取 + * - yes:取统计(默认) * @apiParam {Object} [keys] 搜索条件 * - keys.name: 项目名称 * @apiParam {String} [timerange] 时间范围(如:1678248944,1678248944) @@ -110,6 +116,8 @@ class ProjectController extends AbstractController $type = Request::input('type', 'all'); $archived = Request::input('archived', 'no'); $getcolumn = Request::input('getcolumn', 'no'); + $getuserid = Request::input('getuserid', 'no'); + $getstatistics = Request::input('getstatistics', 'yes'); $keys = Request::input('keys'); $timerange = TimeRange::parse(Request::input('timerange')); // @@ -151,8 +159,15 @@ class ProjectController extends AbstractController } // $list = $builder->orderByDesc('projects.id')->paginate(Base::getPaginate(100, 50)); - $list->transform(function (Project $project) use ($user) { - return array_merge($project->toArray(), $project->getTaskStatistics($user->userid)); + $list->transform(function (Project $project) use ($getstatistics, $getuserid, $user) { + $array = $project->toArray(); + if ($getuserid == 'yes') { + $array['userid_list'] = ProjectUser::whereProjectId($project->id)->pluck('userid')->toArray(); + } + if ($getstatistics == 'yes') { + $array = array_merge($array, $project->getTaskStatistics($user->userid)); + } + return $array; }); // $data = $list->toArray(); diff --git a/package.json b/package.json index f6714bf78..c8d31ed40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "DooTask", - "version": "0.27.31", + "version": "0.27.36", "description": "DooTask is task management system.", "scripts": { "start": "./cmd dev", diff --git a/resources/assets/js/components/UserSelect.vue b/resources/assets/js/components/UserSelect.vue index 7538d4510..9e77896ab 100755 --- a/resources/assets/js/components/UserSelect.vue +++ b/resources/assets/js/components/UserSelect.vue @@ -1,57 +1,151 @@ diff --git a/resources/assets/js/pages/manage/components/DialogSelect.vue b/resources/assets/js/pages/manage/components/DialogSelect.vue deleted file mode 100644 index 670d517a3..000000000 --- a/resources/assets/js/pages/manage/components/DialogSelect.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index ea6a14d8c..7bbad99cc 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -376,16 +376,15 @@ - - -
- - -
-
+ :before-submit="onForward" + :show-select-all="false" + show-dialog + module/> { + if (this.forwardData.length === 0) { + $A.messageError("请选择转发对话或成员"); + reject(); return } - this.forwardLoad = true; + const dialogids = this.forwardData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', '')); + const userids = this.forwardData.filter(value => !$A.leftExists(value, 'd:')); this.$store.dispatch("call", { url: 'dialog/msg/forward', - data: this.forwardData + data: { + dialogids, + userids, + msg_id: this.operateItem.id + } }).then(({data, msg}) => { - this.forwardShow = false; this.$store.dispatch("saveDialogMsg", data.msgs); this.$store.dispatch("updateDialogLastMsg", data.msgs); $A.messageSuccess(msg); + resolve(); }).catch(({msg}) => { $A.modalError(msg); - }).finally(_ => { - this.forwardLoad = false; + reject(); }); - } + }) }, onScroll(event) { @@ -2237,7 +2227,8 @@ export default { break; case "forward": - this.onForward('open') + this.forwardData = []; + this.$refs.forwardSelect.onSelection() break; case "withdraw": diff --git a/resources/assets/js/pages/manage/file.vue b/resources/assets/js/pages/manage/file.vue index b130aed85..47d22db22 100644 --- a/resources/assets/js/pages/manage/file.vue +++ b/resources/assets/js/pages/manage/file.vue @@ -322,16 +322,15 @@ - - -
- - -
-
+ :before-submit="onSendFile" + :show-select-all="false" + show-dialog + module/> import('./components/FilePreview'); @@ -409,7 +407,7 @@ const FileContent = () => import('./components/FileContent'); const FileObject = {sort: null, mode: null, shared: null}; export default { - components: {UserSelect, DialogSelect, PreviewImage, FilePreview, DrawerOverlay, FileContent}, + components: {UserSelect, PreviewImage, FilePreview, DrawerOverlay, FileContent}, directives: {longpress}, data() { return { @@ -477,12 +475,8 @@ export default { shareList: [], shareLoad: 0, - sendShow: false, - sendLoad: false, - sendData: { - dialogids: [], - userids: [], - }, + sendFileId: 0, + sendData: [], linkShow: false, linkData: {}, @@ -1197,12 +1191,9 @@ export default { break; case 'send': - this.sendData = { - dialogids: [], - userids: [], - file_id: item.id - }; - this.sendShow = true; + this.sendFileId = item.id; + this.sendData = []; + this.$refs.sendFile.onSelection() break; case 'share': @@ -1269,24 +1260,31 @@ export default { }, onSendFile() { - if ($A.arrayLength(this.sendData.dialogids) === 0 && $A.arrayLength(this.sendData.userids) === 0) { - $A.messageWarning("请选择转发对话或成员"); - return - } - this.sendLoad = true; - this.$store.dispatch("call", { - url: 'dialog/msg/sendfileid', - data: this.sendData - }).then(({data, msg}) => { - this.sendShow = false; - this.$store.dispatch("saveDialogMsg", data.msgs); - this.$store.dispatch("updateDialogLastMsg", data.msgs); - $A.messageSuccess(msg); - }).catch(({msg}) => { - $A.modalError(msg); - }).finally(_ => { - this.sendLoad = false; - }); + return new Promise((resolve, reject) => { + if (this.sendData.length === 0) { + $A.messageError("请选择转发对话或成员"); + reject(); + return + } + const dialogids = this.sendData.filter(value => $A.leftExists(value, 'd:')).map(value => value.replace('d:', '')); + const userids = this.sendData.filter(value => !$A.leftExists(value, 'd:')); + this.$store.dispatch("call", { + url: 'dialog/msg/sendfileid', + data: { + dialogids, + userids, + file_id: this.sendFileId + } + }).then(({data, msg}) => { + this.$store.dispatch("saveDialogMsg", data.msgs); + this.$store.dispatch("updateDialogLastMsg", data.msgs); + $A.messageSuccess(msg); + resolve(); + }).catch(({msg}) => { + $A.modalError(msg); + reject(); + }); + }) }, linkGet(refresh) { diff --git a/resources/assets/sass/components/user-select.scss b/resources/assets/sass/components/user-select.scss index 7d58bab4f..4d360add1 100755 --- a/resources/assets/sass/components/user-select.scss +++ b/resources/assets/sass/components/user-select.scss @@ -1,152 +1,447 @@ .common-user-select { + &.select-module { + display: none; + } + &.select-border { border: 1px solid #e8e8e8; border-radius: 4px; padding: 0 6px; } + > ul { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; align-items: flex-start; + cursor: pointer; + > li { list-style: none; padding: 0; margin: 3px 6px 3px 0; + &:last-child { margin-right: 0; } + &.add-icon { - cursor: pointer; - border-radius: 50%; width: 26px; height: 26px; background: #F2F3F5 url("data:image/svg+xml;base64,PHN2ZyB0PSIxNjg2NjIxNjA3NDE0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04NzQuNjY2NjY3IDQ3My42aC0zMjQuMjY2NjY3VjE0OS4zMzMzMzNjMC0yMS4zMzMzMzMtMTcuMDY2NjY3LTM4LjQtMzguNC0zOC40cy0zOC40IDE3LjA2NjY2Ny0zOC40IDM4LjR2MzI0LjI2NjY2N0gxNDkuMzMzMzMzYy0yMS4zMzMzMzMgMC0zOC40IDE3LjA2NjY2Ny0zOC40IDM4LjRzMTcuMDY2NjY3IDM4LjQgMzguNCAzOC40aDMyNC4yNjY2Njd2MzI0LjI2NjY2N2MwIDIxLjMzMzMzMyAxNy4wNjY2NjcgMzguNCAzOC40IDM4LjRzMzguNC0xNy4wNjY2NjcgMzguNC0zOC40di0zMjQuMjY2NjY3aDMyNC4yNjY2NjdjMjEuMzMzMzMzIDAgMzguNC0xNy4wNjY2NjcgMzguNC0zOC40cy0xNy4wNjY2NjctMzguNC0zOC40LTM4LjR6IiAgZmlsbD0iIzYwNjI2NiI+PC9wYXRoPjwvc3ZnPg==") no-repeat center; background-size: 50%; - } - } - } -} -.common-user-select-modal { - .ivu-modal-body { - padding: 0 16px !important; - display: flex; - flex-direction: column; - .user-modal-search { - flex-shrink: 0; - position: sticky; - top: 0; - z-index: 9; - padding: 8px 16px; - background: #ffffff; - .search-pre { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - .common-loading { - width: 14px; - height: 14px; - margin: 0; - } - } - } - .user-modal-list { - flex: 1; - display: flex; - flex-direction: column; - max-height: 400px; - ul { - padding: 8px 0; - > li { - list-style: none; - padding: 8px 16px; - margin: 0; - display: flex; - align-items: center; - border-radius: 6px; - cursor: pointer; - &:hover { - background: #f3f3f3; - } - &.selected { - .user-modal-icon { - color: $primary-color; - } - } - &.disabled, - &.disabled:hover { - color: #c5c8ce; - cursor: not-allowed; - } - .user-modal-icon { - flex-shrink: 0; - font-size: 24px; - margin-right: 10px; - color: rgba($primary-desc-color, 0.7); - } - .user-modal-avatar { - flex: 1; - .avatar-name { - margin-left: 10px; - } - } - .user-modal-userid { - flex-shrink: 0; - margin-left: 10px; - font-size: 12px; - color: #cccccc; - transition: margin 0.1s; - } - } - } - } - .user-modal-multiple { - flex-shrink: 0; - position: sticky; - bottom: 0; - z-index: 9; - padding: 6px 16px 0; - background: #ffffff; - color: $primary-desc-color; - display: flex; - align-items: center; - justify-content: center; - .multiple-check { - margin-left: 4px; - } - .multiple-text { - flex: 1; - text-align: right; - line-height: 20px; - font-size: 12px; - padding-left: 6px; - > em { - padding-left: 2px; - font-style: normal; - } - } - } - } - .ivu-modal-fullscreen { - .ivu-modal-content { - margin-top: 46px; - border-top-left-radius: 18px !important; - border-top-right-radius: 18px !important; - .ivu-modal-body { - bottom: 80px; + border-radius: 50%; } } } } -@media screen and (max-width: 576px) { - .common-user-select-modal { +.common-user-select-modal { + .ivu-modal { + max-width: 90%; + + .user-modal-header { + display: flex; + align-items: center; + justify-content: center; + font-weight: 500; + + .user-modal-close { + flex-shrink: 0; + color: $primary-text-color; + } + + .user-modal-title { + flex: 1; + text-align: center; + font-size: 16px; + padding: 0 16px; + color: $primary-title-color; + } + + .user-modal-submit { + flex-shrink: 0; + color: $primary-color; + display: flex; + align-items: center; + + .submit-loading { + width: 14px; + height: 14px; + margin-right: 6px; + } + } + } + .ivu-modal-body { + padding: 0 24px !important; + display: flex; + flex-direction: column; + + .user-modal-search { + flex-shrink: 0; + display: flex; + align-items: center; + + .search-selected { + flex: none; + height: auto; + flex-shrink: 0; + max-width: 60%; + margin-right: 12px; + + ul { + display: flex; + align-items: center; + cursor: pointer; + + > li { + list-style: none; + margin-right: 6px; + + &:last-child { + margin-right: 0; + } + + .img-avatar, + .icon-avatar { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 20px; + background-color: #61B2F9; + color: #ffffff; + + &.department { + background-color: #5BC7B0; + } + + &.project { + background-color: #6E99EB; + } + + &.task { + background-color: #9B96DF; + font-size: 24px; + } + + > img { + width: 100%; + height: 100%; + } + } + } + } + } + + .search-input { + .ivu-input { + height: 36px; + border-color: #f4f5f7; + background-color: #f4f5f7; + } + + .search-pre { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + .common-loading { + width: 14px; + height: 14px; + margin: 0; + } + } + } + } + + .user-modal-switch { + display: flex; + flex-direction: row; + align-items: center; + margin-top: 12px; + border-radius: 4px; + background: #F4F5F7; + + > li { + flex: 1; + list-style: none; + color: $primary-text-color; + border-radius: 2px; + margin: 2px; + padding: 4px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + &.active { + font-weight: 500; + color: $primary-title-color; + background: #ffffff; + } + } + } + .user-modal-list { - max-height: none; + flex: 1; + display: flex; + flex-direction: column; + max-height: 400px; + + ul { + padding: 16px 0; + + &.user-modal-project { + > li { + + &:last-child { + .user-modal-avatar { + .project-name { + &:before { + display: none; + } + } + } + } + } + } + + > li { + list-style: none; + margin: 0; + padding: 16px 0 0; + height: 46px; + box-sizing: content-box; + display: flex; + align-items: center; + cursor: pointer; + + &:first-child { + padding-top: 0; + } + + &.selected { + .user-modal-icon { + color: $primary-color; + } + + .user-modal-avatar { + .project-name { + .subtitle { + > em.all { + display: inline-block; + } + } + } + } + } + + &.somed { + .user-modal-icon { + color: $primary-color; + } + + .user-modal-avatar { + .project-name { + .subtitle { + > em.some { + display: inline-block; + } + } + } + } + } + + &.disabled, + &.disabled:hover { + color: #c5c8ce; + cursor: not-allowed; + } + + .user-modal-icon { + flex-shrink: 0; + font-size: 22px; + margin-right: 10px; + color: rgba($primary-desc-color, 0.7); + } + + .user-modal-all { + font-size: 15px; + font-weight: 500; + } + + .user-modal-avatar { + flex: 1; + display: flex; + align-items: center; + + .img-avatar, + .icon-avatar { + flex-shrink: 0; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 26px; + background-color: #61B2F9; + color: #ffffff; + + &.department { + background-color: #5BC7B0; + } + + &.project { + background-color: #6E99EB; + } + + &.task { + background-color: #9B96DF; + font-size: 24px; + } + + > img { + width: 100%; + height: 100%; + } + } + + .avatar-name, + .project-name { + flex: 1; + width: 0; + margin-left: 12px; + + > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .avatar-name { + display: flex; + align-items: center; + } + + .project-name { + display: flex; + flex-direction: column; + position: relative; + + &:before { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -9px; + height: 1px; + background-color: #f2f2f2; + } + + .label { + font-size: 15px; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .subtitle { + padding-top: 2px; + font-size: 12px; + color: $primary-desc-color; + display: flex; + align-items: center; + + > em { + display: none; + font-style: normal; + padding-left: 4px; + color: $primary-color; + } + } + } + } + } + } + } + + .user-modal-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 36px 36px 20px; + min-height: 200px; + + .empty-icon { + background-color: #f4f5f7; + padding: 20px; + border-radius: 50%; + + .ivu-icon { + color: #d1d8dd; + font-size: 46px; + } + } + + .empty-text { + margin-top: 16px; + color: #bec6cc; + background-color: #f4f5f7; + padding: 4px 15px; + border-radius: 14px; + } + } + } + + .ivu-modal-footer { + border-top: 1px solid #f2f2f2 !important; + padding: 12px 0 !important; + margin: 0 24px !important; + } + + &.ivu-modal-fullscreen { + max-width: none; + + .ivu-modal-content { + margin-top: 46px; + border-top-left-radius: 18px !important; + border-top-right-radius: 18px !important; + } + + .ivu-modal-header { + border-bottom: 1px solid #f2f2f2; + } + + .ivu-modal-body { + top: 60px; + + .user-modal-search { + padding-top: 12px; + } + + .user-modal-list { + max-height: none; + + ul { + > li { + &:last-child { + padding-bottom: 16px; + } + } + } + } } } } diff --git a/resources/assets/sass/dark.scss b/resources/assets/sass/dark.scss index d96d97c78..cc56e8708 100644 --- a/resources/assets/sass/dark.scss +++ b/resources/assets/sass/dark.scss @@ -53,6 +53,7 @@ body.dark-mode-reverse { .overlay-close { > a { color: #323232; + &:hover { color: #000; } @@ -69,6 +70,7 @@ body.dark-mode-reverse { background-color: #000; } } + .teditor-loadedstyle { .tox-sidebar-wrap { box-shadow: none @@ -96,6 +98,7 @@ body.dark-mode-reverse { } } } + .project-column { > ul { > li { @@ -107,6 +110,7 @@ body.dark-mode-reverse { color: #ed4014; background-color: transparent; } + &.today { color: #ff9900; background-color: transparent; @@ -118,6 +122,7 @@ body.dark-mode-reverse { } } } + .project-table { .project-table-body { .task-row { @@ -128,6 +133,7 @@ body.dark-mode-reverse { color: #ed4014; background-color: transparent; } + &.today { color: #ff9900; background-color: transparent; @@ -149,6 +155,7 @@ body.dark-mode-reverse { color: #1c1917; } } + .dialog-title { .main-title { > h2 { @@ -159,22 +166,26 @@ body.dark-mode-reverse { } } } + .dialog-scroller { .dialog-item { .dialog-view { .dialog-head { background-color: #e1e1e1; + .dialog-reply { .reply-desc { color: #ffffff; } } + .dialog-content { .content-text, .content-record, .content-meeting { color: #ffffff; } + .open-approve-details { .cause { border-bottom: 1px solid #7f7f7f; @@ -182,9 +193,11 @@ body.dark-mode-reverse { } } } + .dialog-emoji { > li { background-color: rgba(#f3f3f3, 0.5); + &.hasme { background-color: #f3f3f3; } @@ -192,18 +205,22 @@ body.dark-mode-reverse { } } } + &.self { .dialog-view { .dialog-head { background-color: $primary-color; + .dialog-content { .content-text { > pre { a { color: #0027a1; } + .mention { color: #000000; + &.file, &[data-denotation-char="~"] { color: #0027a1 !important; @@ -212,16 +229,20 @@ body.dark-mode-reverse { } } } + .dialog-emoji { > li { background-color: rgba(#b2ff93, 0.5); + &.hasme { background-color: #b2ff93; } + .emoji-users { &:before { background-color: rgba(#000000, 0.7); } + > ul { > li { color: #000000; @@ -253,73 +274,95 @@ body.dark-mode-reverse { &:before { background-image: url("../images/file/dark/other.svg"); } + &.archive:before { background-image: url("../images/file/dark/archive.svg"); } + &.cad:before { background-image: url("../images/file/dark/cad.svg"); } + &.code:before { background-image: url("../images/file/dark/code.svg"); } + &.document:before { background-image: url("../images/file/dark/document.svg"); } + &.drawio:before { background-image: url("../images/file/dark/drawio.svg"); } + &.excel:before { background-image: url("../images/file/dark/excel.svg"); } + &.flow:before { background-image: url("../images/file/dark/flow.svg"); } + &.folder { &:before { background-image: url("../images/file/dark/folder.svg"); } + &.share { &:before { background-image: url("../images/file/dark/folder-share.svg"); } } } + &.media:before { background-image: url("../images/file/dark/media.svg"); } + &.mind:before { background-image: url("../images/file/dark/mind.svg"); } + &.ofd:before { background-image: url("../images/file/dark/ofd.svg"); } + &.pdf:before { background-image: url("../images/file/dark/pdf.svg"); } + &.picture:before { background-image: url("../images/file/dark/picture.svg"); } + &.ppt:before { background-image: url("../images/file/dark/ppt.svg"); } + &.sheet:before { background-image: url("../images/file/dark/sheet.svg"); } + &.tif:before { background-image: url("../images/file/dark/tif.svg"); } + &.txt:before { background-image: url("../images/file/dark/txt.svg"); } + &.updir:before { background-image: url("../images/file/dark/updir.svg"); } + &.upload:before { background-image: url("../images/file/dark/upload.svg"); } + &.word:before { background-image: url("../images/file/dark/word.svg"); } + &.wps:before { background-image: url("../images/file/dark/wps.svg"); } @@ -353,10 +396,12 @@ body.dark-mode-reverse { color: rgba(0, 0, 0, 0.6); font-size: 12px; } + .block-data { .block-num { color: #000; } + .taskfont { color: rgba(0, 0, 0, 0.7); } @@ -376,6 +421,7 @@ body.dark-mode-reverse { .icon-avatar { color: #1c1917; } + .dialog-box { .dialog-text { color: #555; @@ -402,6 +448,7 @@ body.dark-mode-reverse { .page-index { .page-warp { background-color: #efefef; + .page-header { .header-nav { .header-nav-box { @@ -409,28 +456,33 @@ body.dark-mode-reverse { .header-right-uplog { color: #000000; } + .header-right-1 { .header-right-1-dropdown { color: #000000; } } + .header-right-2 { .header-right-2-dropdown { color: #000000; } } + .header-right-3 { color: #000000; } } } + .header-content { .header-title, .header-tips { color: #000000; } } } - .page-header-bottom{ + + .page-header-bottom { background-color: #efefef; } } @@ -441,16 +493,19 @@ body.dark-mode-reverse { .ql-container { .ql-editor { color: #ccc; + &.ql-blank { &::before { color: #333; } } + .mention { color: #333; } } } + .chat-toolbar { > li { &.chat-send { @@ -462,4 +517,34 @@ body.dark-mode-reverse { } } } + + .common-user-select-modal { + .ivu-modal { + .ivu-modal-body { + .user-modal-search { + .search-selected { + ul { + > li { + .icon-avatar { + color: #1c1917; + } + } + } + } + } + + .user-modal-list { + ul { + > li { + .user-modal-avatar { + .icon-avatar { + color: #1c1917; + } + } + } + } + } + } + } + } } diff --git a/resources/mobile b/resources/mobile index 71ad67db7..777a8d124 160000 --- a/resources/mobile +++ b/resources/mobile @@ -1 +1 @@ -Subproject commit 71ad67db7859b6e4488311a8b03d9f4851627166 +Subproject commit 777a8d12477bda3e34ec6c9290ea5dfd84b8fd81