WIP - start work on keyboard navigation in contacts panel
This commit is contained in:
parent
297fa1af55
commit
08a7543913
13 changed files with 792 additions and 760 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -936,9 +936,11 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"language",
|
||||||
"log",
|
"log",
|
||||||
"picker",
|
"picker",
|
||||||
"postage",
|
"postage",
|
||||||
|
"project",
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#8b8792",
|
"color": "#8b8792",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#e2dfe7",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#5852605c"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#5852605c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#655f6d",
|
"tree_branch_color": "#655f6d",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#7e7887",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#26232a",
|
"background": "#26232a",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#5852603d"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#5852605c"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#5852603d",
|
"background": "#26232a",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#5852603d"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#5852605c"
|
||||||
"color": "#7e7887",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#7e7887",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#585260",
|
"color": "#585260",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#26232a",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#8b87922e"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#8b87922e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#7e7887",
|
"tree_branch_color": "#7e7887",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#655f6d",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#e2dfe7",
|
"background": "#e2dfe7",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#8b87921f"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#8b87922e"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#8b87921f",
|
"background": "#e2dfe7",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#8b87921f"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#8b87922e"
|
||||||
"color": "#655f6d",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#655f6d",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#9c9c9c",
|
"color": "#9c9c9c",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#f1f1f1",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#1c1c1c"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#1c1c1c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#404040",
|
"tree_branch_color": "#404040",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#474747",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#1c1c1c",
|
"background": "#1c1c1c",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#232323"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#2b2b2b"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#232323",
|
"background": "#1c1c1c",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#232323"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#2b2b2b"
|
||||||
"color": "#474747",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#474747",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#474747",
|
"color": "#474747",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#2b2b2b",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#d5d5d5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#d5d5d5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#e3e3e3",
|
"tree_branch_color": "#e3e3e3",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#808080",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#f8f8f8",
|
"background": "#f8f8f8",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#eaeaea"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#e3e3e3"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#eaeaea",
|
"background": "#f8f8f8",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#eaeaea"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#e3e3e3"
|
||||||
"color": "#808080",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#808080",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#93a1a1",
|
"color": "#93a1a1",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#eee8d5",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#586e755c"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#586e755c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#657b83",
|
"tree_branch_color": "#657b83",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#839496",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#073642",
|
"background": "#073642",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#586e753d"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#586e755c"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#586e753d",
|
"background": "#073642",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#586e753d"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#586e755c"
|
||||||
"color": "#839496",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#839496",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#586e75",
|
"color": "#586e75",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#073642",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#93a1a12e"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#93a1a12e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#839496",
|
"tree_branch_color": "#839496",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#657b83",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#eee8d5",
|
"background": "#eee8d5",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#93a1a11f"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#93a1a12e"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#93a1a11f",
|
"background": "#eee8d5",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#93a1a11f"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#93a1a12e"
|
||||||
"color": "#657b83",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#657b83",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#979db4",
|
"color": "#979db4",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#dfe2f1",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#5e66875c"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#5e66875c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#6b7394",
|
"tree_branch_color": "#6b7394",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#898ea4",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#293256",
|
"background": "#293256",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#5e66873d"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#5e66875c"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#5e66873d",
|
"background": "#293256",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#5e66873d"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#5e66875c"
|
||||||
"color": "#898ea4",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#898ea4",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -1249,20 +1249,29 @@
|
||||||
"button_width": 8,
|
"button_width": 8,
|
||||||
"icon_width": 8
|
"icon_width": 8
|
||||||
},
|
},
|
||||||
"row": {
|
"header_row": {
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"row_height": 28,
|
|
||||||
"header": {
|
|
||||||
"family": "Zed Mono",
|
"family": "Zed Mono",
|
||||||
"color": "#5e6687",
|
"color": "#5e6687",
|
||||||
"size": 14,
|
"size": 14,
|
||||||
"margin": {
|
"margin": {
|
||||||
"top": 8
|
"top": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"family": "Zed Mono",
|
||||||
|
"color": "#293256",
|
||||||
|
"size": 14,
|
||||||
|
"background": "#979db42e"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"contact_row": {
|
||||||
|
"padding": {
|
||||||
|
"left": 8
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#979db42e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"row_height": 28,
|
||||||
"tree_branch_color": "#898ea4",
|
"tree_branch_color": "#898ea4",
|
||||||
"tree_branch_width": 1,
|
"tree_branch_width": 1,
|
||||||
"contact_avatar": {
|
"contact_avatar": {
|
||||||
|
@ -1294,26 +1303,7 @@
|
||||||
"button_width": 16,
|
"button_width": 16,
|
||||||
"corner_radius": 8
|
"corner_radius": 8
|
||||||
},
|
},
|
||||||
"project": {
|
"shared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#6b7394",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1332,9 +1322,15 @@
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#dfe2f1",
|
"background": "#dfe2f1",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
|
"hover": {
|
||||||
|
"background": "#979db41f"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"background": "#979db42e"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"hovered_shared_project": {
|
"unshared_project_row": {
|
||||||
"guest_avatar_spacing": 4,
|
"guest_avatar_spacing": 4,
|
||||||
"height": 24,
|
"height": 24,
|
||||||
"guest_avatar": {
|
"guest_avatar": {
|
||||||
|
@ -1352,47 +1348,14 @@
|
||||||
"padding": {
|
"padding": {
|
||||||
"left": 8
|
"left": 8
|
||||||
},
|
},
|
||||||
"background": "#979db41f",
|
"background": "#dfe2f1",
|
||||||
"corner_radius": 6
|
"corner_radius": 6,
|
||||||
},
|
"hover": {
|
||||||
"unshared_project": {
|
"background": "#979db41f"
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
},
|
||||||
"name": {
|
"active": {
|
||||||
"family": "Zed Mono",
|
"background": "#979db42e"
|
||||||
"color": "#6b7394",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"hovered_unshared_project": {
|
|
||||||
"guest_avatar_spacing": 4,
|
|
||||||
"height": 24,
|
|
||||||
"guest_avatar": {
|
|
||||||
"corner_radius": 8,
|
|
||||||
"width": 14
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"family": "Zed Mono",
|
|
||||||
"color": "#6b7394",
|
|
||||||
"size": 14,
|
|
||||||
"margin": {
|
|
||||||
"right": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"padding": {
|
|
||||||
"left": 8
|
|
||||||
},
|
|
||||||
"corner_radius": 6
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_finder": {
|
"contact_finder": {
|
||||||
|
|
|
@ -21,3 +21,8 @@ futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
language = { path = "../language", features = ["test-support"] }
|
||||||
|
project = { path = "../project", features = ["test-support"] }
|
||||||
|
workspace = { path = "../workspace", features = ["test-support"] }
|
||||||
|
|
|
@ -15,6 +15,7 @@ use serde::Deserialize;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::IconButton;
|
use theme::IconButton;
|
||||||
|
use workspace::menu::{SelectNext, SelectPrev};
|
||||||
use workspace::{AppState, JoinProject};
|
use workspace::{AppState, JoinProject};
|
||||||
|
|
||||||
impl_actions!(
|
impl_actions!(
|
||||||
|
@ -22,12 +23,13 @@ impl_actions!(
|
||||||
[RequestContact, RemoveContact, RespondToContactRequest]
|
[RequestContact, RemoveContact, RespondToContactRequest]
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum ContactEntry {
|
enum ContactEntry {
|
||||||
Header(&'static str),
|
Header(&'static str),
|
||||||
IncomingRequest(Arc<User>),
|
IncomingRequest(Arc<User>),
|
||||||
OutgoingRequest(Arc<User>),
|
OutgoingRequest(Arc<User>),
|
||||||
Contact(Arc<Contact>),
|
Contact(Arc<Contact>),
|
||||||
|
ContactProject(Arc<Contact>, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ContactsPanel {
|
pub struct ContactsPanel {
|
||||||
|
@ -36,6 +38,7 @@ pub struct ContactsPanel {
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
filter_editor: ViewHandle<Editor>,
|
filter_editor: ViewHandle<Editor>,
|
||||||
|
selection: Option<usize>,
|
||||||
_maintain_contacts: Subscription,
|
_maintain_contacts: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +60,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ContactsPanel::remove_contact);
|
cx.add_action(ContactsPanel::remove_contact);
|
||||||
cx.add_action(ContactsPanel::respond_to_contact_request);
|
cx.add_action(ContactsPanel::respond_to_contact_request);
|
||||||
cx.add_action(ContactsPanel::clear_filter);
|
cx.add_action(ContactsPanel::clear_filter);
|
||||||
|
cx.add_action(ContactsPanel::select_next);
|
||||||
|
cx.add_action(ContactsPanel::select_prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactsPanel {
|
impl ContactsPanel {
|
||||||
|
@ -72,6 +77,7 @@ impl ContactsPanel {
|
||||||
|
|
||||||
cx.subscribe(&user_query_editor, |this, _, event, cx| {
|
cx.subscribe(&user_query_editor, |this, _, event, cx| {
|
||||||
if let editor::Event::BufferEdited = event {
|
if let editor::Event::BufferEdited = event {
|
||||||
|
this.selection.take();
|
||||||
this.update_entries(cx)
|
this.update_entries(cx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -88,17 +94,20 @@ impl ContactsPanel {
|
||||||
let theme = &theme.contacts_panel;
|
let theme = &theme.contacts_panel;
|
||||||
let current_user_id =
|
let current_user_id =
|
||||||
this.user_store.read(cx).current_user().map(|user| user.id);
|
this.user_store.read(cx).current_user().map(|user| user.id);
|
||||||
|
let is_selected = this.selection == Some(ix);
|
||||||
|
|
||||||
match &this.entries[ix] {
|
match &this.entries[ix] {
|
||||||
ContactEntry::Header(text) => {
|
ContactEntry::Header(text) => {
|
||||||
Label::new(text.to_string(), theme.header.text.clone())
|
let header_style =
|
||||||
|
theme.header_row.style_for(&Default::default(), is_selected);
|
||||||
|
Label::new(text.to_string(), header_style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.aligned()
|
.aligned()
|
||||||
.left()
|
.left()
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_height(theme.row_height)
|
.with_height(theme.row_height)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(theme.header.container)
|
.with_style(header_style.container)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
||||||
|
@ -106,6 +115,7 @@ impl ContactsPanel {
|
||||||
this.user_store.clone(),
|
this.user_store.clone(),
|
||||||
theme,
|
theme,
|
||||||
true,
|
true,
|
||||||
|
is_selected,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
ContactEntry::OutgoingRequest(user) => Self::render_contact_request(
|
ContactEntry::OutgoingRequest(user) => Self::render_contact_request(
|
||||||
|
@ -113,18 +123,36 @@ impl ContactsPanel {
|
||||||
this.user_store.clone(),
|
this.user_store.clone(),
|
||||||
theme,
|
theme,
|
||||||
false,
|
false,
|
||||||
|
is_selected,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
ContactEntry::Contact(contact) => Self::render_contact(
|
ContactEntry::Contact(contact) => {
|
||||||
contact.clone(),
|
Self::render_contact(contact.clone(), theme, is_selected)
|
||||||
current_user_id,
|
}
|
||||||
app_state.clone(),
|
ContactEntry::ContactProject(contact, project_ix) => {
|
||||||
theme,
|
let is_last_project_for_contact =
|
||||||
cx,
|
this.entries.get(ix + 1).map_or(true, |next| {
|
||||||
),
|
if let ContactEntry::ContactProject(next_contact, _) = next {
|
||||||
|
next_contact.user.id != contact.user.id
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self::render_contact_project(
|
||||||
|
contact.clone(),
|
||||||
|
current_user_id,
|
||||||
|
*project_ix,
|
||||||
|
app_state.clone(),
|
||||||
|
theme,
|
||||||
|
is_last_project_for_contact,
|
||||||
|
is_selected,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
selection: None,
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
match_candidates: Default::default(),
|
match_candidates: Default::default(),
|
||||||
filter_editor: user_query_editor,
|
filter_editor: user_query_editor,
|
||||||
|
@ -137,175 +165,173 @@ impl ContactsPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_contact(
|
fn render_contact(
|
||||||
|
contact: Arc<Contact>,
|
||||||
|
theme: &theme::ContactsPanel,
|
||||||
|
is_selected: bool,
|
||||||
|
) -> ElementBox {
|
||||||
|
Flex::row()
|
||||||
|
.with_children(contact.user.avatar.clone().map(|avatar| {
|
||||||
|
Image::new(avatar)
|
||||||
|
.with_style(theme.contact_avatar)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.boxed()
|
||||||
|
}))
|
||||||
|
.with_child(
|
||||||
|
Label::new(
|
||||||
|
contact.user.github_login.clone(),
|
||||||
|
theme.contact_username.text.clone(),
|
||||||
|
)
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.contact_username.container)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.row_height)
|
||||||
|
.contained()
|
||||||
|
.with_style(
|
||||||
|
*theme
|
||||||
|
.contact_row
|
||||||
|
.style_for(&Default::default(), is_selected),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_contact_project(
|
||||||
contact: Arc<Contact>,
|
contact: Arc<Contact>,
|
||||||
current_user_id: Option<u64>,
|
current_user_id: Option<u64>,
|
||||||
|
project_ix: usize,
|
||||||
app_state: Arc<AppState>,
|
app_state: Arc<AppState>,
|
||||||
theme: &theme::ContactsPanel,
|
theme: &theme::ContactsPanel,
|
||||||
|
is_last_project: bool,
|
||||||
|
is_selected: bool,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
let project_count = contact.non_empty_projects().count();
|
let project = &contact.projects[project_ix];
|
||||||
|
let project_id = project.id;
|
||||||
|
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
let line_height = theme.unshared_project.name.text.line_height(font_cache);
|
|
||||||
let cap_height = theme.unshared_project.name.text.cap_height(font_cache);
|
|
||||||
let baseline_offset = theme.unshared_project.name.text.baseline_offset(font_cache)
|
|
||||||
+ (theme.unshared_project.height - line_height) / 2.;
|
|
||||||
let tree_branch_width = theme.tree_branch_width;
|
|
||||||
let tree_branch_color = theme.tree_branch_color;
|
|
||||||
let host_avatar_height = theme
|
let host_avatar_height = theme
|
||||||
.contact_avatar
|
.contact_avatar
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
|
let row = &theme.unshared_project_row.default;
|
||||||
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
let baseline_offset =
|
||||||
|
row.name.text.baseline_offset(font_cache) + (row.height - line_height) / 2.;
|
||||||
|
let tree_branch_width = theme.tree_branch_width;
|
||||||
|
let tree_branch_color = theme.tree_branch_color;
|
||||||
|
|
||||||
Flex::column()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Flex::row()
|
Canvas::new(move |bounds, _, cx| {
|
||||||
.with_children(contact.user.avatar.clone().map(|avatar| {
|
let start_x = bounds.min_x() + (bounds.width() / 2.) - (tree_branch_width / 2.);
|
||||||
Image::new(avatar)
|
let end_x = bounds.max_x();
|
||||||
.with_style(theme.contact_avatar)
|
let start_y = bounds.min_y();
|
||||||
.aligned()
|
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
|
||||||
.left()
|
|
||||||
.boxed()
|
cx.scene.push_quad(gpui::Quad {
|
||||||
}))
|
bounds: RectF::from_points(
|
||||||
.with_child(
|
vec2f(start_x, start_y),
|
||||||
Label::new(
|
vec2f(
|
||||||
contact.user.github_login.clone(),
|
start_x + tree_branch_width,
|
||||||
theme.contact_username.text.clone(),
|
if is_last_project {
|
||||||
)
|
end_y
|
||||||
.contained()
|
} else {
|
||||||
.with_style(theme.contact_username.container)
|
bounds.max_y()
|
||||||
.aligned()
|
},
|
||||||
.left()
|
),
|
||||||
.boxed(),
|
),
|
||||||
)
|
background: Some(tree_branch_color),
|
||||||
.constrained()
|
border: gpui::Border::default(),
|
||||||
.with_height(theme.row_height)
|
corner_radius: 0.,
|
||||||
.boxed(),
|
});
|
||||||
|
cx.scene.push_quad(gpui::Quad {
|
||||||
|
bounds: RectF::from_points(
|
||||||
|
vec2f(start_x, end_y),
|
||||||
|
vec2f(end_x, end_y + tree_branch_width),
|
||||||
|
),
|
||||||
|
background: Some(tree_branch_color),
|
||||||
|
border: gpui::Border::default(),
|
||||||
|
corner_radius: 0.,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.constrained()
|
||||||
|
.with_width(host_avatar_height)
|
||||||
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_children(
|
.with_child({
|
||||||
contact
|
let is_host = Some(contact.user.id) == current_user_id;
|
||||||
.non_empty_projects()
|
let is_guest = !is_host
|
||||||
.enumerate()
|
&& project
|
||||||
.map(|(ix, project)| {
|
.guests
|
||||||
let project_id = project.id;
|
.iter()
|
||||||
|
.any(|guest| Some(guest.id) == current_user_id);
|
||||||
|
let is_shared = project.is_shared;
|
||||||
|
let app_state = app_state.clone();
|
||||||
|
|
||||||
|
MouseEventHandler::new::<JoinProject, _, _>(
|
||||||
|
project_id as usize,
|
||||||
|
cx,
|
||||||
|
|mouse_state, _| {
|
||||||
|
let style = if project.is_shared {
|
||||||
|
&theme.shared_project_row
|
||||||
|
} else {
|
||||||
|
&theme.unshared_project_row
|
||||||
|
}
|
||||||
|
.style_for(mouse_state, is_selected);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Canvas::new(move |bounds, _, cx| {
|
Label::new(
|
||||||
let start_x = bounds.min_x() + (bounds.width() / 2.)
|
project.worktree_root_names.join(", "),
|
||||||
- (tree_branch_width / 2.);
|
style.name.text.clone(),
|
||||||
let end_x = bounds.max_x();
|
)
|
||||||
let start_y = bounds.min_y();
|
.aligned()
|
||||||
let end_y =
|
.left()
|
||||||
bounds.min_y() + baseline_offset - (cap_height / 2.);
|
.contained()
|
||||||
|
.with_style(style.name.container)
|
||||||
cx.scene.push_quad(gpui::Quad {
|
|
||||||
bounds: RectF::from_points(
|
|
||||||
vec2f(start_x, start_y),
|
|
||||||
vec2f(
|
|
||||||
start_x + tree_branch_width,
|
|
||||||
if ix + 1 == project_count {
|
|
||||||
end_y
|
|
||||||
} else {
|
|
||||||
bounds.max_y()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
background: Some(tree_branch_color),
|
|
||||||
border: gpui::Border::default(),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
cx.scene.push_quad(gpui::Quad {
|
|
||||||
bounds: RectF::from_points(
|
|
||||||
vec2f(start_x, end_y),
|
|
||||||
vec2f(end_x, end_y + tree_branch_width),
|
|
||||||
),
|
|
||||||
background: Some(tree_branch_color),
|
|
||||||
border: gpui::Border::default(),
|
|
||||||
corner_radius: 0.,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.constrained()
|
|
||||||
.with_width(host_avatar_height)
|
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child({
|
.with_children(project.guests.iter().filter_map(|participant| {
|
||||||
let is_host = Some(contact.user.id) == current_user_id;
|
participant.avatar.clone().map(|avatar| {
|
||||||
let is_guest = !is_host
|
Image::new(avatar)
|
||||||
&& project
|
.with_style(style.guest_avatar)
|
||||||
.guests
|
.aligned()
|
||||||
.iter()
|
.left()
|
||||||
.any(|guest| Some(guest.id) == current_user_id);
|
.contained()
|
||||||
let is_shared = project.is_shared;
|
.with_margin_right(style.guest_avatar_spacing)
|
||||||
let app_state = app_state.clone();
|
.boxed()
|
||||||
|
|
||||||
MouseEventHandler::new::<ContactsPanel, _, _>(
|
|
||||||
project_id as usize,
|
|
||||||
cx,
|
|
||||||
|mouse_state, _| {
|
|
||||||
let style = match (project.is_shared, mouse_state.hovered) {
|
|
||||||
(false, false) => &theme.unshared_project,
|
|
||||||
(false, true) => &theme.hovered_unshared_project,
|
|
||||||
(true, false) => &theme.shared_project,
|
|
||||||
(true, true) => &theme.hovered_shared_project,
|
|
||||||
};
|
|
||||||
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
Label::new(
|
|
||||||
project.worktree_root_names.join(", "),
|
|
||||||
style.name.text.clone(),
|
|
||||||
)
|
|
||||||
.aligned()
|
|
||||||
.left()
|
|
||||||
.contained()
|
|
||||||
.with_style(style.name.container)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
.with_children(project.guests.iter().filter_map(
|
|
||||||
|participant| {
|
|
||||||
participant.avatar.clone().map(|avatar| {
|
|
||||||
Image::new(avatar)
|
|
||||||
.with_style(style.guest_avatar)
|
|
||||||
.aligned()
|
|
||||||
.left()
|
|
||||||
.contained()
|
|
||||||
.with_margin_right(
|
|
||||||
style.guest_avatar_spacing,
|
|
||||||
)
|
|
||||||
.boxed()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.contained()
|
|
||||||
.with_style(style.container)
|
|
||||||
.constrained()
|
|
||||||
.with_height(style.height)
|
|
||||||
.boxed()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.with_cursor_style(if !is_host && is_shared {
|
|
||||||
CursorStyle::PointingHand
|
|
||||||
} else {
|
|
||||||
CursorStyle::Arrow
|
|
||||||
})
|
})
|
||||||
.on_click(move |_, cx| {
|
}))
|
||||||
if !is_host && !is_guest {
|
.contained()
|
||||||
cx.dispatch_global_action(JoinProject {
|
.with_style(style.container)
|
||||||
project_id,
|
|
||||||
app_state: app_state.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flex(1., true)
|
|
||||||
.boxed()
|
|
||||||
})
|
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_height(theme.unshared_project.height)
|
.with_height(style.height)
|
||||||
.boxed()
|
.boxed()
|
||||||
}),
|
},
|
||||||
)
|
)
|
||||||
.contained()
|
.with_cursor_style(if !is_host && is_shared {
|
||||||
.with_style(theme.row.clone())
|
CursorStyle::PointingHand
|
||||||
|
} else {
|
||||||
|
CursorStyle::Arrow
|
||||||
|
})
|
||||||
|
.on_click(move |_, cx| {
|
||||||
|
if !is_host && !is_guest {
|
||||||
|
cx.dispatch_global_action(JoinProject {
|
||||||
|
project_id,
|
||||||
|
app_state: app_state.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flex(1., true)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.constrained()
|
||||||
|
.with_height(row.height)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +340,7 @@ impl ContactsPanel {
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
theme: &theme::ContactsPanel,
|
theme: &theme::ContactsPanel,
|
||||||
is_incoming: bool,
|
is_incoming: bool,
|
||||||
|
is_selected: bool,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
enum Reject {}
|
enum Reject {}
|
||||||
|
@ -409,7 +436,11 @@ impl ContactsPanel {
|
||||||
row.constrained()
|
row.constrained()
|
||||||
.with_height(theme.row_height)
|
.with_height(theme.row_height)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(theme.row)
|
.with_style(
|
||||||
|
*theme
|
||||||
|
.contact_row
|
||||||
|
.style_for(&Default::default(), is_selected),
|
||||||
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,6 +449,7 @@ impl ContactsPanel {
|
||||||
let query = self.filter_editor.read(cx).text(cx);
|
let query = self.filter_editor.read(cx).text(cx);
|
||||||
let executor = cx.background().clone();
|
let executor = cx.background().clone();
|
||||||
|
|
||||||
|
let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned());
|
||||||
self.entries.clear();
|
self.entries.clear();
|
||||||
|
|
||||||
let mut request_entries = Vec::new();
|
let mut request_entries = Vec::new();
|
||||||
|
@ -443,13 +475,11 @@ impl ContactsPanel {
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
executor.clone(),
|
executor.clone(),
|
||||||
));
|
));
|
||||||
if !matches.is_empty() {
|
request_entries.extend(
|
||||||
request_entries.extend(
|
matches
|
||||||
matches.iter().map(|mat| {
|
.iter()
|
||||||
ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())
|
.map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outgoing = user_store.outgoing_contact_requests();
|
let outgoing = user_store.outgoing_contact_requests();
|
||||||
|
@ -474,13 +504,11 @@ impl ContactsPanel {
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
executor.clone(),
|
executor.clone(),
|
||||||
));
|
));
|
||||||
if !matches.is_empty() {
|
request_entries.extend(
|
||||||
request_entries.extend(
|
matches
|
||||||
matches.iter().map(|mat| {
|
.iter()
|
||||||
ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())
|
.map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !request_entries.is_empty() {
|
if !request_entries.is_empty() {
|
||||||
|
@ -515,22 +543,33 @@ impl ContactsPanel {
|
||||||
.iter()
|
.iter()
|
||||||
.partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
|
.partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
|
||||||
|
|
||||||
if !online_contacts.is_empty() {
|
for (matches, name) in [(online_contacts, "Online"), (offline_contacts, "Offline")] {
|
||||||
self.entries.push(ContactEntry::Header("Online"));
|
if !matches.is_empty() {
|
||||||
self.entries.extend(
|
self.entries.push(ContactEntry::Header(name));
|
||||||
online_contacts
|
for mat in matches {
|
||||||
.into_iter()
|
let contact = &contacts[mat.candidate_id];
|
||||||
.map(|mat| ContactEntry::Contact(contacts[mat.candidate_id].clone())),
|
self.entries.push(ContactEntry::Contact(contact.clone()));
|
||||||
);
|
self.entries
|
||||||
|
.extend(contact.projects.iter().enumerate().filter_map(
|
||||||
|
|(ix, project)| {
|
||||||
|
if project.worktree_root_names.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ContactEntry::ContactProject(contact.clone(), ix))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !offline_contacts.is_empty() {
|
if let Some(selection) = &mut self.selection {
|
||||||
self.entries.push(ContactEntry::Header("Offline"));
|
for (ix, entry) in self.entries.iter().enumerate() {
|
||||||
self.entries.extend(
|
if Some(entry) == prev_selected_entry.as_ref() {
|
||||||
offline_contacts
|
*selection = ix;
|
||||||
.into_iter()
|
break;
|
||||||
.map(|mat| ContactEntry::Contact(contacts[mat.candidate_id].clone())),
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,6 +605,30 @@ impl ContactsPanel {
|
||||||
self.filter_editor
|
self.filter_editor
|
||||||
.update(cx, |editor, cx| editor.set_text("", cx));
|
.update(cx, |editor, cx| editor.set_text("", cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(ix) = self.selection {
|
||||||
|
if self.entries.len() > ix + 1 {
|
||||||
|
self.selection = Some(ix + 1);
|
||||||
|
}
|
||||||
|
} else if !self.entries.is_empty() {
|
||||||
|
self.selection = Some(0);
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
self.list_state.reset(self.entries.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(ix) = self.selection {
|
||||||
|
if ix > 0 {
|
||||||
|
self.selection = Some(ix - 1);
|
||||||
|
} else {
|
||||||
|
self.selection = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
self.list_state.reset(self.entries.len());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element {
|
fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element {
|
||||||
|
@ -633,4 +696,249 @@ impl View for ContactsPanel {
|
||||||
.with_style(theme.container)
|
.with_style(theme.container)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.focus(&self.filter_editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
|
||||||
|
let mut cx = Self::default_keymap_context();
|
||||||
|
cx.set.insert("menu".into());
|
||||||
|
cx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ContactEntry {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
ContactEntry::Header(name_1) => {
|
||||||
|
if let ContactEntry::Header(name_2) = other {
|
||||||
|
return name_1 == name_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactEntry::IncomingRequest(user_1) => {
|
||||||
|
if let ContactEntry::IncomingRequest(user_2) = other {
|
||||||
|
return user_1.id == user_2.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactEntry::OutgoingRequest(user_1) => {
|
||||||
|
if let ContactEntry::OutgoingRequest(user_2) = other {
|
||||||
|
return user_1.id == user_2.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactEntry::Contact(contact_1) => {
|
||||||
|
if let ContactEntry::Contact(contact_2) = other {
|
||||||
|
return contact_1.user.id == contact_2.user.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactEntry::ContactProject(contact_1, ix_1) => {
|
||||||
|
if let ContactEntry::ContactProject(contact_2, ix_2) = other {
|
||||||
|
return contact_1.user.id == contact_2.user.id && ix_1 == ix_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use client::{proto, test::FakeServer, ChannelList, Client};
|
||||||
|
use gpui::TestAppContext;
|
||||||
|
use language::LanguageRegistry;
|
||||||
|
use theme::ThemeRegistry;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_contact_panel(cx: &mut TestAppContext) {
|
||||||
|
let (app_state, server) = init(cx).await;
|
||||||
|
let panel = cx.add_view(0, |cx| ContactsPanel::new(app_state.clone(), cx));
|
||||||
|
|
||||||
|
let get_users_request = server.receive::<proto::GetUsers>().await.unwrap();
|
||||||
|
server
|
||||||
|
.respond(
|
||||||
|
get_users_request.receipt(),
|
||||||
|
proto::UsersResponse {
|
||||||
|
users: [
|
||||||
|
"user_zero",
|
||||||
|
"user_one",
|
||||||
|
"user_two",
|
||||||
|
"user_three",
|
||||||
|
"user_four",
|
||||||
|
"user_five",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(id, name)| proto::User {
|
||||||
|
id: id as u64,
|
||||||
|
github_login: name.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
server.send(proto::UpdateContacts {
|
||||||
|
incoming_requests: vec![proto::IncomingContactRequest {
|
||||||
|
requester_id: 1,
|
||||||
|
should_notify: false,
|
||||||
|
}],
|
||||||
|
outgoing_requests: vec![2],
|
||||||
|
contacts: vec![
|
||||||
|
proto::Contact {
|
||||||
|
user_id: 3,
|
||||||
|
online: true,
|
||||||
|
projects: vec![proto::ProjectMetadata {
|
||||||
|
id: 101,
|
||||||
|
worktree_root_names: vec!["dir1".to_string()],
|
||||||
|
is_shared: true,
|
||||||
|
guests: vec![2],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
proto::Contact {
|
||||||
|
user_id: 4,
|
||||||
|
online: true,
|
||||||
|
projects: vec![proto::ProjectMetadata {
|
||||||
|
id: 102,
|
||||||
|
worktree_root_names: vec!["dir2".to_string()],
|
||||||
|
is_shared: true,
|
||||||
|
guests: vec![2],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
proto::Contact {
|
||||||
|
user_id: 5,
|
||||||
|
online: false,
|
||||||
|
projects: vec![],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.foreground().run_until_parked();
|
||||||
|
assert_eq!(
|
||||||
|
render_to_strings(&panel, cx),
|
||||||
|
&[
|
||||||
|
"+",
|
||||||
|
"v Requests",
|
||||||
|
" incoming user_one <=== selected",
|
||||||
|
" outgoing user_two",
|
||||||
|
"v Online",
|
||||||
|
" user_four",
|
||||||
|
" dir2",
|
||||||
|
" user_three",
|
||||||
|
" dir1",
|
||||||
|
"v Offline",
|
||||||
|
" user_five",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.update(cx, |panel, cx| {
|
||||||
|
panel
|
||||||
|
.filter_editor
|
||||||
|
.update(cx, |editor, cx| editor.set_text("f", cx))
|
||||||
|
});
|
||||||
|
cx.foreground().run_until_parked();
|
||||||
|
assert_eq!(
|
||||||
|
render_to_strings(&panel, cx),
|
||||||
|
&[
|
||||||
|
"+",
|
||||||
|
"Online",
|
||||||
|
" user_four <=== selected",
|
||||||
|
" dir2",
|
||||||
|
"Offline",
|
||||||
|
" user_five",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.update(cx, |panel, cx| {
|
||||||
|
panel.select_next(&Default::default(), cx);
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
render_to_strings(&panel, cx),
|
||||||
|
&[
|
||||||
|
"+",
|
||||||
|
"Online",
|
||||||
|
" user_four",
|
||||||
|
" dir2 <=== selected",
|
||||||
|
"Offline",
|
||||||
|
" user_five",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.update(cx, |panel, cx| {
|
||||||
|
panel.select_next(&Default::default(), cx);
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
render_to_strings(&panel, cx),
|
||||||
|
&[
|
||||||
|
"+",
|
||||||
|
"Online",
|
||||||
|
" user_four",
|
||||||
|
" dir2",
|
||||||
|
"Offline",
|
||||||
|
" user_five <=== selected",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_to_strings(panel: &ViewHandle<ContactsPanel>, cx: &TestAppContext) -> Vec<String> {
|
||||||
|
panel.read_with(cx, |panel, _| {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
entries.push("+".to_string());
|
||||||
|
entries.extend(panel.entries.iter().map(|entry| match entry {
|
||||||
|
ContactEntry::Header(name) => {
|
||||||
|
format!("{}", name)
|
||||||
|
}
|
||||||
|
ContactEntry::IncomingRequest(user) => {
|
||||||
|
format!(" incoming {}", user.github_login)
|
||||||
|
}
|
||||||
|
ContactEntry::OutgoingRequest(user) => {
|
||||||
|
format!(" outgoing {}", user.github_login)
|
||||||
|
}
|
||||||
|
ContactEntry::Contact(contact) => {
|
||||||
|
format!(" {}", contact.user.github_login)
|
||||||
|
}
|
||||||
|
ContactEntry::ContactProject(contact, project_ix) => {
|
||||||
|
format!(
|
||||||
|
" {}",
|
||||||
|
contact.projects[*project_ix].worktree_root_names.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
entries
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init(cx: &mut TestAppContext) -> (Arc<AppState>, FakeServer) {
|
||||||
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
let themes = ThemeRegistry::new((), cx.font_cache());
|
||||||
|
let fs = project::FakeFs::new(cx.background().clone());
|
||||||
|
let languages = Arc::new(LanguageRegistry::test());
|
||||||
|
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||||
|
let mut client = Client::new(http_client.clone());
|
||||||
|
let server = FakeServer::for_client(100, &mut client, &cx).await;
|
||||||
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||||
|
let channel_list =
|
||||||
|
cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
|
||||||
|
|
||||||
|
let get_channels = server.receive::<proto::GetChannels>().await.unwrap();
|
||||||
|
server
|
||||||
|
.respond(get_channels.receipt(), Default::default())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
(
|
||||||
|
Arc::new(AppState {
|
||||||
|
languages,
|
||||||
|
themes,
|
||||||
|
client,
|
||||||
|
user_store: user_store.clone(),
|
||||||
|
fs,
|
||||||
|
channel_list,
|
||||||
|
build_window_options: || unimplemented!(),
|
||||||
|
build_workspace: |_, _, _| unimplemented!(),
|
||||||
|
}),
|
||||||
|
server,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,11 +235,13 @@ pub struct CommandPalette {
|
||||||
pub struct ContactsPanel {
|
pub struct ContactsPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub header: ContainedText,
|
|
||||||
pub user_query_editor: FieldEditor,
|
pub user_query_editor: FieldEditor,
|
||||||
pub user_query_editor_height: f32,
|
pub user_query_editor_height: f32,
|
||||||
pub add_contact_button: IconButton,
|
pub add_contact_button: IconButton,
|
||||||
pub row: ContainerStyle,
|
pub header_row: Interactive<ContainedText>,
|
||||||
|
pub contact_row: Interactive<ContainerStyle>,
|
||||||
|
pub shared_project_row: Interactive<ProjectRow>,
|
||||||
|
pub unshared_project_row: Interactive<ProjectRow>,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
pub contact_avatar: ImageStyle,
|
pub contact_avatar: ImageStyle,
|
||||||
pub contact_username: ContainedText,
|
pub contact_username: ContainedText,
|
||||||
|
@ -247,10 +249,6 @@ pub struct ContactsPanel {
|
||||||
pub disabled_contact_button: IconButton,
|
pub disabled_contact_button: IconButton,
|
||||||
pub tree_branch_width: f32,
|
pub tree_branch_width: f32,
|
||||||
pub tree_branch_color: Color,
|
pub tree_branch_color: Color,
|
||||||
pub shared_project: ProjectRow,
|
|
||||||
pub hovered_shared_project: ProjectRow,
|
|
||||||
pub unshared_project: ProjectRow,
|
|
||||||
pub hovered_unshared_project: ProjectRow,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default)]
|
||||||
|
|
|
@ -21,16 +21,6 @@ export default function contactsPanel(theme: Theme) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sharedProject = {
|
|
||||||
...project,
|
|
||||||
background: backgroundColor(theme, 300),
|
|
||||||
cornerRadius: 6,
|
|
||||||
name: {
|
|
||||||
...project.name,
|
|
||||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const contactButton = {
|
const contactButton = {
|
||||||
background: backgroundColor(theme, 100),
|
background: backgroundColor(theme, 100),
|
||||||
color: iconColor(theme, "primary"),
|
color: iconColor(theme, "primary"),
|
||||||
|
@ -62,14 +52,21 @@ export default function contactsPanel(theme: Theme) {
|
||||||
buttonWidth: 8,
|
buttonWidth: 8,
|
||||||
iconWidth: 8,
|
iconWidth: 8,
|
||||||
},
|
},
|
||||||
row: {
|
headerRow: {
|
||||||
padding: { left: 8 },
|
|
||||||
},
|
|
||||||
rowHeight: 28,
|
|
||||||
header: {
|
|
||||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||||
margin: { top: 8 },
|
margin: { top: 8 },
|
||||||
|
active: {
|
||||||
|
...text(theme, "mono", "primary", { size: "sm" }),
|
||||||
|
background: backgroundColor(theme, 100, "active"),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
contactRow: {
|
||||||
|
padding: { left: 8 },
|
||||||
|
active: {
|
||||||
|
background: backgroundColor(theme, 100, "active"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowHeight: 28,
|
||||||
treeBranchColor: borderColor(theme, "muted"),
|
treeBranchColor: borderColor(theme, "muted"),
|
||||||
treeBranchWidth: 1,
|
treeBranchWidth: 1,
|
||||||
contactAvatar: {
|
contactAvatar: {
|
||||||
|
@ -93,17 +90,35 @@ export default function contactsPanel(theme: Theme) {
|
||||||
background: backgroundColor(theme, 100),
|
background: backgroundColor(theme, 100),
|
||||||
color: iconColor(theme, "muted"),
|
color: iconColor(theme, "muted"),
|
||||||
},
|
},
|
||||||
project,
|
sharedProjectRow: {
|
||||||
sharedProject,
|
|
||||||
hoveredSharedProject: {
|
|
||||||
...sharedProject,
|
|
||||||
background: backgroundColor(theme, 300, "hovered"),
|
|
||||||
cornerRadius: 6,
|
|
||||||
},
|
|
||||||
unsharedProject: project,
|
|
||||||
hoveredUnsharedProject: {
|
|
||||||
...project,
|
...project,
|
||||||
|
background: backgroundColor(theme, 300),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
|
name: {
|
||||||
|
...project.name,
|
||||||
|
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
background: backgroundColor(theme, 300, "hovered"),
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
background: backgroundColor(theme, 300, "active"),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
unsharedProjectRow: {
|
||||||
|
...project,
|
||||||
|
background: backgroundColor(theme, 300),
|
||||||
|
cornerRadius: 6,
|
||||||
|
name: {
|
||||||
|
...project.name,
|
||||||
|
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
background: backgroundColor(theme, 300, "hovered"),
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
background: backgroundColor(theme, 300, "active"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue