From 54cd8cfa3c3b6b7b7116f3d5fd0c7c333baa348a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 3 Apr 2017 10:29:11 -0400 Subject: [PATCH 1/4] Add blacklist controls to 1:1 conversation view. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 + .../ic_block.imageset/Contents.json | 23 ++ .../ic_block.imageset/ic_block@1x.png | Bin 0 -> 1856 bytes .../ic_block.imageset/ic_block@2x.png | Bin 0 -> 2668 bytes .../ic_block.imageset/ic_block@3x.png | Bin 0 -> 3657 bytes .../table_ic_block.imageset/Contents.json | 23 ++ .../table_ic_block@1x.png | Bin 0 -> 1433 bytes .../table_ic_block@2x.png | Bin 0 -> 1856 bytes .../table_ic_block@3x.png | Bin 0 -> 2290 bytes Signal/src/Storyboard/Main.storyboard | 55 ++- .../AddToBlockListViewController.m | 93 +---- Signal/src/ViewControllers/BlockListUIUtils.h | 33 ++ Signal/src/ViewControllers/BlockListUIUtils.m | 241 ++++++++++++ .../ViewControllers/BlockListViewController.m | 57 +-- .../DebugUITableViewController.m | 70 ++-- ...SConversationSettingsTableViewController.h | 8 +- ...SConversationSettingsTableViewController.m | 357 ++++++++++-------- .../ViewControllers/OWSTableViewController.h | 19 +- .../ViewControllers/OWSTableViewController.m | 89 ++++- .../translations/en.lproj/Localizable.strings | 33 +- 20 files changed, 749 insertions(+), 358 deletions(-) create mode 100644 Signal/Images.xcassets/ic_block.imageset/Contents.json create mode 100644 Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png create mode 100644 Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png create mode 100644 Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png create mode 100644 Signal/Images.xcassets/table_ic_block.imageset/Contents.json create mode 100644 Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png create mode 100644 Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@2x.png create mode 100644 Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png create mode 100644 Signal/src/ViewControllers/BlockListUIUtils.h create mode 100644 Signal/src/ViewControllers/BlockListUIUtils.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b7da912f83..4a133156aa 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 34330A5E1E787BD800DF2FB9 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */; }; 34330A611E788EA900DF2FB9 /* AttachmentUploadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330A601E788EA900DF2FB9 /* AttachmentUploadView.m */; }; 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; }; + 343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */; }; 344F2F671E57A932000D9322 /* UIViewController+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 344F2F661E57A932000D9322 /* UIViewController+OWS.m */; }; 34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; }; 345671011E89A5F1006EE662 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671001E89A5F1006EE662 /* ThreadUtil.m */; }; @@ -347,6 +348,8 @@ 34330A601E788EA900DF2FB9 /* AttachmentUploadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentUploadView.m; sourceTree = ""; }; 34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = ""; }; 34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = ""; }; + 343D3D991E9283F100165CA4 /* BlockListUIUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockListUIUtils.h; sourceTree = ""; }; + 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlockListUIUtils.m; sourceTree = ""; }; 344F2F651E57A932000D9322 /* UIViewController+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+OWS.h"; path = "util/UIViewController+OWS.h"; sourceTree = ""; }; 344F2F661E57A932000D9322 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "util/UIViewController+OWS.m"; sourceTree = ""; }; 34535D801E256BE9008A4747 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+OWS.h"; sourceTree = ""; }; @@ -823,6 +826,8 @@ 34B3F8381E8DF1700035BE1A /* AttachmentApprovalViewController.swift */, 34B3F8391E8DF1700035BE1A /* AttachmentSharing.h */, 34B3F83A1E8DF1700035BE1A /* AttachmentSharing.m */, + 343D3D991E9283F100165CA4 /* BlockListUIUtils.h */, + 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */, 34B3F89A1E8DF3270035BE1A /* BlockListViewController.h */, 34B3F89B1E8DF3270035BE1A /* BlockListViewController.m */, 34B3F83B1E8DF1700035BE1A /* CallViewController.swift */, @@ -1958,6 +1963,7 @@ 34B3F88F1E8DF1710035BE1A /* RegistrationViewController.m in Sources */, 34B3F8901E8DF1710035BE1A /* SettingsTableViewController.m in Sources */, 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */, + 343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */, 34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */, 76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */, 76EB058A18170B33006006FC /* Release.m in Sources */, diff --git a/Signal/Images.xcassets/ic_block.imageset/Contents.json b/Signal/Images.xcassets/ic_block.imageset/Contents.json new file mode 100644 index 0000000000..9f5a8e00eb --- /dev/null +++ b/Signal/Images.xcassets/ic_block.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_block@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_block@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_block@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..488f96f0d0ce73b5198e86e9b251d2feed40eb64 GIT binary patch literal 1856 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wk0|SdvW=KRygs+cPa(=E}VoH8es$NBI0Z=sqgH44MkeQoWlBiITo0C^; zRbi_HR$&EXgM{^!6u?SKvTcwsYk*NEcCio^nlW#B-B_{|37a;u=! z;{2RaP!NRXWtP|(*?>KSE{q5fh%V>++=8Oi;$omSJ5#6@WHEI05eRGS%wcvQ3!-cA zFUkb^G!f)3J42`i$YSW~Be7Y4EQq856!caBnH8xy5iXg)00sNP80adj66hM8^K`Ye5o1R}Ho%5|=ed5=a_TlC6qMld@8iOORp<7-!(L@06IXk0flPk5#n| zFeO;|=BH$)Rk|dWq}mx77@F%EnCKc9g&0^^8JJj^7})5esX=l9NXEG+HL)bWC?r2W z#}4d^To6~mSkDZi6ImXr)kYtbi;!{^B+G(DfqB-B3#b#8|LwS##e|9&7?`$ux;TbJ za6X;B-#^4r;JBsf4ab$7VNn_z9UthNEd9gm?p!*zo@wbulZ~oL&*vvN{Sf{$amCyY zF|{KXA|#v{lwFGJ_8mW${BG~v+MUNU7f!A*`ka=Y_Wa%3x9@y?{=B~=^_Ba@_utn~ z?Bq31P<*oAwZ7?X*y^p~HIIXRtT?qdaK_Azm- z34Fj9Q`TCSSl=M~L+DTVh0O)RdaU!W7~Xll)uv=$)#T3T4;*`BK7NpR*OZ?T5MY;a z*h;2B{OYN?8w=NQ#C^VbW4D35$dpQjGRC+D@!Hv6osJ3p`7raw?+sD&YsDP@nAk82 zUyZTq?ucM{kzJE2`QrQsTO0Ys)f2ZY>HeWvA^B-($nHmKP4BD#-3oTK)7K8L8eh0RcdhN;BewiLHU5V08joFhAguN6 zLL}$bd%G7T>D_m@&3c3HdCc;(%W5aSnRZ`)Exp2LTG5=vonD^z4`iFK+H0%LJ8Q=) zu6>2yR-WAbr8|Io*(MHcPm3*V3crOzbuSz;(-cb#oILT<`3ri|u0E#}%x|+i?w;^p za+QT+opO}Fuf*r)vl4QceI7{%W-Q1K%*dTzyzyj^-J{d@TecPEK5tK$bhqnt`$WD; zE3dQ|+JD@~DVt(-!RJ%^L@uF7yS~yTe#h0ke|B6~zb^Q0=|78YO(|3LRd&VnY|?n% zdH?I?)yuA~zshoaT}qU&)zw=8mY1J0$6hbVwPw;ksU7)ko*Qdv?7k`gj(is^JFqQ0 zH)eAIw_cOyX781$-j`k2|J~Sn=ls+ynGV)BxpEFg{GX8$@3=-;>c{W;vq`;wjQ8Dt XVpY9&IoI;1plZ$2)z4*}Q$iB}FR88r literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a87f1e33420cbd5a6995c5f11a2cb469650fc047 GIT binary patch literal 2668 zcmZ`*3pkVeAK&IWtg;Zx>1c+;HiTH)VutFF+gw7IZ6;&eyS8C=q%9q#yUR(Th&o-| zayp5MLzE;Eijz+2xI{})E}`__=(4}3-sgEQzwh_+`F*~h@B4nA=S}wB=nm6BXn;T< z*!p#3U)kMBeyXd=t{0g*p2}`uzOOq8RNQPhC|f|e>o)O0AkD?{6Aa4C)(3%{(K)}Uev9YnSnAlYqfVUlM?da%; zwX(t5*q~(yG(V0bpbF6(zVX~9U-lt0_;enVD_{Z~Bl*5m8W1fYAdzyRug6@S0%rI( zB@X|yEtx^AJcG5ySYf|MV+ff)pvf~oq=nJHy5dIjShJqO=vW4e!Des-d>POByK=JL zekJ~2kwPkWPHeV`bKdas%5lC7KER5Wx4@sn6cBCjvxeq}zjb7e>s zQxs$K@j>5Yh^xP7Cwf!qV;D1#n^oOrtb+T1{DbM*5kaHLIx{{%#<~B z)`h(-9{b(Ie4-1$0(gF0DxD!OY?h)Bnvb59+4LhBg@ghzjE>_+(;^vkfx_F8y|dYG zpG4h`RS4s;-$mnPUjoh(2xErFk*NX((Zv}G{=56+$rBUX zud)p8h$Ud9tP_#|zgs0mMKJBCwL;w+kn!EtMw4*MM3YPEudfCdsToFPJXX)$HkqJw zL+N&nc5Ysd3Nem+?#_F}y~O*~fs6jUy`x9-w>OYClKVBrEd_V>#y7kuhBbm3V2K9Y zh3zJ9i}Y)k?pKFrLSmi$@`C(!nn_UOk|ftpz7s2}k_I3g0YakwC?~$px zwaU{Q9MPxulvFyA_Y~v$?0s%BZfG1nlBialx@+(~q!)-CafaxSr)4!lw=G%~ zF4G3Nw9T#RP~}GiYR&IXkL=UPpxd?8qM4T}ea8*?c$F#7o7%ifr@SvNPZ-|w!1huv z&#YhtTN#cCZfGU`djc5VrB$}OE2)<8*sip6V}#TKTGtIr0AYO4Asr^K);P!M1f>~d zScb4<8(BJ*ldl{Mj*GpL|3T8I$rd&3tZdB9m&{b9IK5J;MmSY=r~L70YxWZmroxpz z+y^;Xbu{;h*zFXi;&};9dW)xSc@5tZb2@!-ddtufsK`Z-94WFszIMZ2v2?nmqxov$ z0-R0@kgeROo*v~x@e@WIZf(e8#LQUK4-A^I5GKx!kiM6xX+Pz7O?PkK56ce8KOc{1 zq|#Pf`;VDdB1~omTSStwBMpDuEv?1ArxarbFQSvgy89=)`s~^tp`dlaCONvs=ZYtm zECJJ#b`T{@!E{y@O5#x%+HDy=h|E3j_fK3cD*bMkt5|R4=DZ8iQ)idM3qye=nq2K+ zx{vvq?uMq9j``S&6&vAqeEePq)42`2QtKo!LBD>X#R2Wh?-1+nEC~utDOxy@jL`}h zM~>DNe9T!U$^COuIo`EWy`os`+B$TN649n#Eh2U5s){1Z;evCqhXN^Sk(!#8!HK6U zE=6a$=>|fGw))ppLVY<_XGTlQl4=o=%hp*~vwnMXs%k-Jl)0x%Oa>XY*L~y}Vo}2g zt2|u*`(TzPE_#IAoEMyDN{;uVWyU-{xrNpV|MUR0Eq~0#v!epV+2CRR->uc=BI{5$ z08w`Y3I5nveD`wM-~h{N)bl`;M)38jNL^Yw5?o2Io_c-G^FV=3_LQER-az*j3#Csl z79pQ&#XSKZyN7zQufCyT~OY zH^!A6)JUimO9{JN>zqqmyv)iFnbNU}Jsu{|?vO!8jq=M>*xFs@K%(r%KVu@fJyT(xt!i(RWc3l#gMilcxH#g~;CP@7(?NI{pl1 zB-oxElmgZG^JDIQSG1k}lPZ&vB03ko&1o8RHQ!p$w!g`18#JQ7tTd-dJIY8&RaJ{c bQ=L)r?P`injZ&$X{{pUe-AFDbZQK1nHN-D} literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a141bd0ce8fe4ed3b7124a4e19a60df738c480c9 GIT binary patch literal 3657 zcmZ`+3p|ti8{bAWmoVn8W{57kxz5Zoo7-_)a?4sZGg-#gHd(s36Dp3fO4LM1hn%jW zWO7M2q#P>3a*&Ej(&f~DQ|BE2KArdTc`v``dA`5z^L^g;d4KPlMe*}cS20up008P< zp5y@8d9!?9tRy>*9Er!uPC$Nuhby4wp7Dfi0OoqG=K}z$+VUL;C@j(k02JY@z)(S` zuMdvFi9ymMIN?mBFor9O1_1Cvoa{1&DWJoIG3;1APUsAukHE>U*hqbK(TfaJZc4pY2|!9X$D%ot{@fG@*geo7~+ z?LWZ(OHxSZej}SN;x}!0dFHqPCZEHOlb0YUmL(uq~|I!c~{^nR{{1M>KW66}#<#`b>-w77levDuLGlGT21ps;JaTJ!2$qps6 zWCfj9;b@OX{nYUzki=ngc`B1+aw1rfJIHhalVFX;I3Uor2()z|8jC~Q;;>je>W7#G z8OS{(9+NKM@B%p;Hep^xjC=^QLCOmCqyIY;kCJ!Mg0A}3vh!A1&k|H*$iMrapu#p? zH7I+7G@%DlwXvZG}ZblTdKIb|_stwa+Nr@Tr@JH2`} z*Bh?MtZ+?%IMh?X+e$QHYu6f(!@P9Jk>)pHbDs*|-%Q;T^!`bLo&7tDWwgNJot-mP zgVofr-pTG#vO%&nh^csM@gXfMGm86A>^_%ugT;;8q#0~u#muHitp1WT#%3d(J;};L zEFoxVYWwi%^73+{&vy~L!vN4=PSlFJo|WveDQ>0ssWPH2J1>8m&a2dhm3S9de@aaQF5$Kon;;Nc*&1rakD-l)1mCwQUsV{>4RoUi$n%Nc@#8 zbUPq>woY#)P(u>gr)1ge#a;bE!>DyBF4PkeK_U>RE1Jw|wTe!?w&Y@rfo6%G4|EAj z^d!}_-So4&a-^xyL=c9pgjXsZ(Nuq$mZh#R(Xgl{X^o^ZFSkCrQv)4kz0Yvh?kZ6; zkhLSNX3uoooXtMa2b?2fC+Xd0Pooz}jdkSIi__hdMc}36_kcRX=jX<9mKg5y>B7hV zp^w&lmE73po!TF(KH<6fwqo7DAK+)B4Uw!K-p#sAe~cfu z;hYNf@on|fVTH%&v2}L%H~SdRcs2VyJ9wgrlohEWbsan?wi&*fd483)vnsj_dVB_b z6JGe5R(bbLGTKeuDmi%Q+arL<!GcmNLFU^*~Wgl;(cBKT-QZ$*$g$bm| znV0!!lbIfq{OLlAj|E3g5B-Me>fRt65YM2k?^*ia)WL!Kp6V@4LRv-gY2BB7;LiS* zqKbg2w{@?sdYf8rGJ6ZcPvnoznN2f{JMLasG&{LtW~b6)==m`fzDntadyZX|;W&{Q)Jz=;NQWy;a*UyLG!Xz%7Ye%2l%m(rWwakVL~y zYSke#3-Qolvz*Rs+RO;@+~tmf@B^EYs_E;uo0RSzzNg5JO@1?w?u-07xwIlA=+evW z@9u5$^=-IUR*%~nf8V1dA9kXoTuh^=DM+=2Nkil7$EF{_7O|&B19;9uM|Q+_BR!xe zg3y1agzL{e!LA09nhkrSf?C5?TZKWmk473&&W857g@Q8fyILe7Tf(YBYR!$eFUpi= z)A@NDLO~Y{yB9%kRR!Aw>sWykN9w|dni)h89W2CFuD!cw09r+{->zd09^-DnaEWd= z;5s!r(w1bUrhJ50S6!*vKDCmJavgzP1s>g<|l)9#4Y9Z3fcK#v74>kIA(;;~- zp&-FQy1{U$yEpNp*;J<&WOd`JjAVy1bTHPBbnQ)RdKNOd?7$cdtosy7YQ3*f=$bB? zi1O{ou(T+6h-!CzM)RO&-*fX0qHT`uSDY# z4r%Pyyhgnp5P0&}31(%l7TasaDftUkd@8tiy$F&39=XuCkFlv2r&n8Tl&`07;8dei z`t5GlB3>{M{?^SRqnqJUUVSQS{;Gbp(033M{e-gTvO3OYn^DR zz`lQ2;~deT{E6WU`U{b)JN~A}Nq1`EX4vtX`!Tmq8|2V>{c2Z;%)2^am5^Pk!Ruf0 zZ&e$DB@LBTOHJeu)|g?!E}TYW_wjj%}p=yOSQ_;5M8#w zP`WB9_=H=6Q(o>y=!@v0O#Jo_%ZaM$7l$aS8i=~Rd814EZ#>VoGWoQ69Zh=Hv{-j; z_@E-<;=_FEDtq%k7wL{SG}Tk6Imh(OBYjd$7{~k}nhvJ@>5twR*aR2)Tv<`Z{L*Qw zf2O67L4D%oIUBU7Kyf@F>S)HbqbbKwSC1SG)iHX3Z8MJwBNUHGtqN6kZwx8DeBe$r zIylR&{gQiU+RVm~b^Af?s8mANbRggAisB{K1^$Ef62pGxX{U6aGv;vsBaON4tU^z; zgy%qREpP1Fav@b{Y5$fpUKSbNLEQG>@N7)Q%d{4?w=eXmk)&&$U3xpCH0$P?d-g?* z=yxeOiBW=vns`)MSV-WcMZNNki(LtsIpYXHt=+jx*Y8}yL|Ds~2^W*1!Uip6!FAkXjil(KIr-=YIklDM?ylcd?r=PX?(y$ zvsLzeJYA*q%+`oH46oGVP&Xc={1I|7UFBnV<(9f$0~&eKwXyYMNA8OawA}uz^mV1M|f_iH)oF@RAb-@Zheeb@QzK?HPCMF5`TC{AqWgOz zobO1PUiQ~k8AZpu5~|p-8$1kY3>Brp2d@aj7_+666EifZ zXwA3}?5c~aK=i!L@9ZvC#={e~%kIuvSsAA&m!#CRrq@#mOZZ<8{@E9La-Yk>FDR~_ a`%q$i$(lBM58|l&|IW+Jk6h!rIpu#4VEcyv literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/table_ic_block.imageset/Contents.json b/Signal/Images.xcassets/table_ic_block.imageset/Contents.json new file mode 100644 index 0000000000..d3f232d578 --- /dev/null +++ b/Signal/Images.xcassets/table_ic_block.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "table_ic_block@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "table_ic_block@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "table_ic_block@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..2c874458ba837b3e8f3d451ce1e55e4137cffb0a GIT binary patch literal 1433 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z81_llpi<;HsXMd|v6mX?-X(Gs7c7{+3kj2o|M`E)8SrADBDCn&MGAmMZB3v?o0Sfkop_z?7Rta>C&iOg{ zMZpD$$*CZRfwdqBp{oX46N$?jBnc#qDalsFrAb+-$t6g!1&lLr+ILD!*GCez(Z{OV z2AC48eDhN>(<)sOOH%EO3=GY64NP^kZ@qa7`C_$Wb6FF2r|G)#Jv7#^{VY8`2rusyBSpI<})&RpoK@E#Fnfdk>uO zuUqx&`uet&D>G-+FIxMxJl5Q8{(;{i^{R5Wx7B(-uUb&ZaQ%a9*Ljy>)9mIf^H#+> zP8*wpE&NRC8=cN?DCc~1O3UJK<@tt&eODN+xyl}l|J6`!(H>tAIO7uIUxu$%%i{}X zIDTB<6mwv*VWgfTzsP0{J4K5Pfnq0DPk-Tg&wX$C&c++9dmc{BHPEt^wmn`oF5E% rqRwARmE1?|*MG{jUv{rJRwsYk*NEcCio^nlW#B-B_{|37a;u=! z;{2RaP!NRXWtP|(*?>KSE{q5fh%V>++=8Oi;$omSJ5#6@WHEI05eRGS%wcvQ3!-cA zFUkb^G!f)3J42`i$YSW~Be7Y4EQq856!caBnH8xy5iXg)00sNP80adj66hM8^K`Ye5o1R}Ho%5|=ed5=a_TlC6qMld@8iOORp<7-!(L@06IXk0flPk5#n| zFeO;|=BH$)Rk|dWq}mx77@F%EnCKc9g&3Gy8JJjEnAqr}sX=l9NXEG+HL)bWC?r2W z#}4d^To6~mSkDZi6ImXr)kYtbi;!{^B+G(DfqB-B3#b#8|LwRsYy68C7?`$ux;TbJ za6X;B-#^4r;JBsf4ab$7VNn_z9UthNEd9gm?p!*zo@wbulZ~oL&*vvN{Sf{$amCyY zF|{KXA|#v{lwFGJ_8mW${BG~v+MUNU7f!A*`ka=Y_Wa%3x9@y?{=B~=^_Ba@_utn~ z?Bq31P<*oAwZ7?X*y^p~HIIXRtT?qdaK_Azm- z34Fj9Q`TCSSl=M~L+DTVh0O)RdaU!W7~Xll)uv=$)#T3T4;*`BK7NpR*OZ?T5MY;a z*h;2B{OYN?8w=NQ#C^VbW4D35$dpQjGRC+D@!Hv6osJ3p`7raw?+sD&YsDP@nAk82 zUyZTq?ucM{kzJE2`QrQsTO0Ys)f2ZY>HeWvA^B-($nHmKP4BD#-3oTK)7K8L8eh0RcdhN;BewiLHU5V08joFhAguN6 zLL}$bd%G7T>D_m@&3c3HdCc;(%W5aSnRZ`)Exp2LTG5=vonD^z4`iFK+H0%LJ8Q=) zu6>2yR-WAbr8|Io*(MHcPm3*V3crOzbuSz;(-cb#oILT<`3ri|u0E#}%x|+i?w;^p za+QT+opO}Fuf*r)vl4QceI7{%W-Q1K%*dTzyzyj^-J{d@TecPEK5tK$bhqnt`$WD; zE3dQ|+JD@~DVt(-!RJ%^L@uF7yS~yTe#h0ke|B6~zb^Q0=|78YO(|3LRd&VnY|?n% zdH?I?)yuA~zshoaT}qU&)zw=8mY1J0$6hbVwPw;ksU7)ko*Qdv?7k`gj(is^JFqQ0 zH)eAIw_cOyX781$-j`k2|J~Sn=ls+ynGV)BxpEFg{GX8$@3=-;>c{W;vq`;wjQ8Dt XVpY9&IoI;1plZ$2)z4*}Q$iB}jS{aj literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8a225841c58541d0b9100d69bfc9bf431c2a7dfc GIT binary patch literal 2290 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wk0|SdvW=KRygs+cPa(=E}VoH8es$NBI0Z=sqgH44MkeQoWlBiITo0C^; zRbi_HR$&EXgM{^!6u?SKvTcwsYk*NEcCio^nlW#B-B_{|37a;u=! z;{2RaP!NRXWtP|(*?>KSE{q5fh%V>++=8Oi;$omSJ5#6@WHEI05eRGS%wcvQ3!-cA zFUkb^G!f)3J42`i$YSW~Be7Y4EQq856!caBnH8xy5iXg)00sNP#KJ}&s|316=lq=f zqTqtW-m&{c!2iNs|Mk_3{*lw_;o(xj}^&9Q?K>r=>mv!<=wnrF z15624zWFJcX_YRCC8>5s28QOk1}3@&Mj-~~Rt6?kMkY4;XljsL0FrSoN=+=uFAB-e z&#?phA{WF}FxE4J=tP!>YPHb^NpVc_>+L_*5bCiwVfGBa-8VX2|8=%RXhgUgdN^zm-r}+P z*eQ2SA)PHB*18x5Uik6*(7UH8@8;}HfB%lPm8a&-O-tkZXWraAZkcYLduu|kcEC$6 z6Rk?==U1;@l?(apD1U>Qtucjh59|5^NsBL5oZl$6GKRrZ`{CsWA`%Q*i#(?MDK6l@ zvvkH$rN`^OPW)k5z+A&Rx2xu8;hOBusHCQ^4W}Qlukb#idai77`en^>zlOvQOiNDX z^R$KtelM80X3~7dj`PkoyxUxv{y6<-v0>SDJib{~;U?oZMq6d|1A-ewD>gNKQaXPu zbp2*!*^~YvF^BxW_%Qq?vgn5M(R&l)WXc<-PcyXm?^LbwC@zZkdf^AP z2j^#>N(@(T2`_4ES{zruc? z>0R6h!InjHW?4Sjxkbismq1nOTkmTEd%G@7I@ zXLW0)#CZ3&IV$HKt*NZli*k;6zocW1LfIBp8>6;Y*OQh^VQ~$qRTJ55;C_4MT7K>7 zwMtL-EK7G1AIf^kqi5>gM~2lqN^VyG6Pn?E z6hmXqzdTqo-{&vRM7KGpxm`z>E* zf0f+9uk}A|ZH&W=1$p}CLIZ#~{FCjRb$?%|9c#;pY19gTpf45@^4)b#YzL?z;_2$= Jvd$@?2>^s6bhZEh literal 0 HcmV?d00001 diff --git a/Signal/src/Storyboard/Main.storyboard b/Signal/src/Storyboard/Main.storyboard index 7c24dbcc5e..2459c564ba 100644 --- a/Signal/src/Storyboard/Main.storyboard +++ b/Signal/src/Storyboard/Main.storyboard @@ -446,18 +446,40 @@ - + + + + + + + + + + + + + + + + + - + - + @@ -506,20 +528,20 @@ - + - + @@ -528,7 +550,7 @@ - + @@ -544,7 +566,7 @@ - + @@ -555,7 +577,7 @@ - + @@ -575,7 +597,7 @@ - + @@ -592,7 +614,7 @@ - + @@ -622,22 +644,21 @@ + - - - + diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index ccc5fc3693..b557277430 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -3,6 +3,7 @@ // #import "AddToBlockListViewController.h" +#import "BlockListUIUtils.h" #import "ContactTableViewCell.h" #import "CountryCodeViewController.h" #import "Environment.h" @@ -13,8 +14,8 @@ #import "UIUtil.h" #import "UIView+OWS.h" #import "ViewControllerUtils.h" -#import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -273,25 +274,17 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI PhoneNumber *parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:possiblePhoneNumber]; OWSAssert(parsedPhoneNumber); - [_blockingManager addBlockedPhoneNumber:[parsedPhoneNumber toE164]]; - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_TITLE", - @"The title of the 'phone number blocked' alert in the block view.") - message:[NSString - stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'phone number blocked' alert in " - @"the block view. Embeds {{the blocked phone number}}."), - [parsedPhoneNumber toE164]] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; - - _phoneNumberTextField.text = nil; + __weak AddToBlockListViewController *weakSelf = self; + [BlockListUIUtils showBlockPhoneNumberActionSheet:[parsedPhoneNumber toE164] + displayName:[parsedPhoneNumber toE164] + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + if (isBlocked) { + // Clear phone number text field is block succeeds. + weakSelf.phoneNumberTextField.text = nil; + } + }]; } - (void)textFieldDidChange:(id)sender @@ -417,62 +410,10 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI [tableView deselectRowAtIndexPath:indexPath animated:YES]; Contact *contact = self.contacts[(NSUInteger)indexPath.item]; - [self showBlockActionSheet:contact]; -} - -- (void)showBlockActionSheet:(Contact *)contact -{ - OWSAssert(contact); - - NSString *displayName = contact.fullName; - - NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", - @"A format for the 'block phone number' action sheet title."), - displayName]; - - UIAlertController *actionSheetController = - [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak AddToBlockListViewController *weakSelf = self; - UIAlertAction *unblockAction = [UIAlertAction - actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [weakSelf blockContact:contact displayName:displayName]; - }]; - [actionSheetController addAction:unblockAction]; - - UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") - style:UIAlertActionStyleCancel - handler:nil]; - [actionSheetController addAction:dismissAction]; - - [self presentViewController:actionSheetController animated:YES completion:nil]; -} - -- (void)blockContact:(Contact *)contact displayName:(NSString *)displayName -{ - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - if (phoneNumber.toE164.length > 0) { - [_blockingManager addBlockedPhoneNumber:phoneNumber.toE164]; - } - } - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_TITLE", - @"The title of the 'contact blocked' alert in the block view.") - message:[NSString stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'contact blocked' " - @"alert in the block view. It is populated with the " - @"blocked contact's name."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; + [BlockListUIUtils showBlockContactActionSheet:contact + fromViewController:self + blockingManager:_blockingManager + completionBlock:nil]; } #pragma mark - UIScrollViewDelegate diff --git a/Signal/src/ViewControllers/BlockListUIUtils.h b/Signal/src/ViewControllers/BlockListUIUtils.h new file mode 100644 index 0000000000..23fb34975a --- /dev/null +++ b/Signal/src/ViewControllers/BlockListUIUtils.h @@ -0,0 +1,33 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import + +@class Contact; +@class OWSBlockingManager; + +typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); + +@interface BlockListUIUtils : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (void)showBlockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + ++ (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + ++ (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + +@end diff --git a/Signal/src/ViewControllers/BlockListUIUtils.m b/Signal/src/ViewControllers/BlockListUIUtils.m new file mode 100644 index 0000000000..713cb0136b --- /dev/null +++ b/Signal/src/ViewControllers/BlockListUIUtils.m @@ -0,0 +1,241 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "BlockListUIUtils.h" +#import "PhoneNumber.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation BlockListUIUtils + ++ (void)showBlockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + NSMutableArray *phoneNumbers = [NSMutableArray new]; + for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { + if (phoneNumber.toE164.length > 0) { + [phoneNumbers addObject:phoneNumber.toE164]; + } + } + if (phoneNumbers.count < 1) { + [self showBlockFailedAlert:fromViewController]; + if (completionBlock) { + completionBlock(NO); + } + return; + } + [self showBlockPhoneNumbersActionSheet:phoneNumbers + displayName:contact.fullName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + ++ (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + [self showBlockPhoneNumbersActionSheet:@[ phoneNumber ] + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + ++ (void)showBlockPhoneNumbersActionSheet:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + OWSAssert(phoneNumbers.count > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", + @"A format for the 'block user' action sheet title."), + displayName]; + + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + UIAlertAction *unblockAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [self blockPhoneNumbers:phoneNumbers + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager]; + if (completionBlock) { + completionBlock(YES); + } + }]; + [actionSheetController addAction:unblockAction]; + + UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + if (completionBlock) { + completionBlock(NO); + } + }]; + [actionSheetController addAction:dismissAction]; + + [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; +} + ++ (void)blockPhoneNumbers:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager +{ + OWSAssert(phoneNumbers.count > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + for (NSString *phoneNumber in phoneNumbers) { + OWSAssert(phoneNumber.length > 0); + [blockingManager addBlockedPhoneNumber:phoneNumber]; + } + + [self showOkAlertWithTitle:NSLocalizedString( + @"BLOCK_LIST_VIEW_BLOCKED_ALERT_TITLE", @"The title of the 'user blocked' alert.") + message:[NSString + stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT", + @"The message format of the 'user blocked' " + @"alert. Embeds {{the blocked user's name or phone number}}."), + displayName] + fromViewController:fromViewController]; +} + ++ (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + OWSAssert(phoneNumber.length > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", + @"A format for the 'unblock user' action sheet title."), + displayName]; + + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + UIAlertAction *unblockAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [BlockListUIUtils unblockPhoneNumber:phoneNumber + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager]; + if (completionBlock) { + completionBlock(NO); + } + }]; + [actionSheetController addAction:unblockAction]; + + UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + if (completionBlock) { + completionBlock(YES); + } + }]; + [actionSheetController addAction:dismissAction]; + + [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; +} + ++ (void)unblockPhoneNumber:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager +{ + OWSAssert(phoneNumber.length > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + [blockingManager removeBlockedPhoneNumber:phoneNumber]; + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE", + @"The title of the 'user unblocked' alert.") + message:[NSString + stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", + @"The message format of the 'user unblocked' " + @"alert. It is populated with the " + @"blocked phone number."), + displayName] + fromViewController:fromViewController]; +} + ++ (void)showBlockFailedAlert:(UIViewController *)fromViewController +{ + OWSAssert(fromViewController); + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_TITLE", + @"The title of the 'block user failed' alert.") + message:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_MESSAGE", + @"The title of the 'block user failed' alert.") + fromViewController:fromViewController]; +} + ++ (void)showUnblockFailedAlert:(UIViewController *)fromViewController +{ + OWSAssert(fromViewController); + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_TITLE", + @"The title of the 'unblock user failed' alert.") + message:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_MESSAGE", + @"The title of the 'unblock user failed' alert.") + fromViewController:fromViewController]; +} + ++ (void)showOkAlertWithTitle:(NSString *)title + message:(NSString *)message + fromViewController:(UIViewController *)fromViewController +{ + OWSAssert(title.length > 0); + OWSAssert(message.length > 0); + OWSAssert(fromViewController); + + UIAlertController *controller = + [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + + [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) + style:UIAlertActionStyleDefault + handler:nil]]; + [fromViewController presentViewController:controller animated:YES completion:nil]; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/BlockListViewController.m b/Signal/src/ViewControllers/BlockListViewController.m index 701a85549f..8732e09361 100644 --- a/Signal/src/ViewControllers/BlockListViewController.m +++ b/Signal/src/ViewControllers/BlockListViewController.m @@ -4,6 +4,7 @@ #import "BlockListViewController.h" #import "AddToBlockListViewController.h" +#import "BlockListUIUtils.h" #import "Environment.h" #import "OWSContactsManager.h" #import "PhoneNumber.h" @@ -168,7 +169,11 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { case BlockListViewControllerSection_BlockList: { NSString *phoneNumber = _blockedPhoneNumbers[(NSUInteger)indexPath.item]; NSString *displayName = [self displayNameForIndexPath:indexPath]; - [self showUnblockActionSheet:phoneNumber displayName:displayName]; + [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber + displayName:displayName + fromViewController:self + blockingManager:_blockingManager + completionBlock:nil]; break; } default: @@ -176,56 +181,6 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { } } -- (void)showUnblockActionSheet:(NSString *)phoneNumber displayName:(NSString *)displayName -{ - OWSAssert(phoneNumber.length > 0); - OWSAssert(displayName.length > 0); - - NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", - @"A format for the 'unblock phone number' action sheet title."), - displayName]; - - UIAlertController *actionSheetController = - [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak BlockListViewController *weakSelf = self; - UIAlertAction *unblockAction = [UIAlertAction - actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [weakSelf unblockPhoneNumber:phoneNumber displayName:displayName]; - }]; - [actionSheetController addAction:unblockAction]; - - UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") - style:UIAlertActionStyleCancel - handler:nil]; - [actionSheetController addAction:dismissAction]; - - [self presentViewController:actionSheetController animated:YES completion:nil]; -} - -- (void)unblockPhoneNumber:(NSString *)phoneNumber displayName:(NSString *)displayName -{ - [_blockingManager removeBlockedPhoneNumber:phoneNumber]; - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE", - @"The title of the 'phone number unblocked' alert in the block view.") - message:[NSString stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'phone number unblocked' " - @"alert in the block view. It is populated with the " - @"blocked phone number."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; -} - #pragma mark - Actions - (void)blockedPhoneNumbersDidChange:(id)notification diff --git a/Signal/src/ViewControllers/DebugUITableViewController.m b/Signal/src/ViewControllers/DebugUITableViewController.m index 5203bf91f5..2af90b643f 100644 --- a/Signal/src/ViewControllers/DebugUITableViewController.m +++ b/Signal/src/ViewControllers/DebugUITableViewController.m @@ -34,41 +34,45 @@ NS_ASSUME_NONNULL_BEGIN OWSTableContents *contents = [OWSTableContents new]; contents.title = @"Debug: Conversation"; - - [contents addSection:[OWSTableSection sectionWithTitle:@"Messages View" - items:@[ - [OWSTableItem actionWithTitle:@"Send 10 messages (1/sec.)" - actionBlock:^{ - [DebugUITableViewController sendTextMessage:10 - thread:thread]; - }], - [OWSTableItem actionWithTitle:@"Send 100 messages (1/sec.)" - actionBlock:^{ - [DebugUITableViewController sendTextMessage:100 - thread:thread]; - }], - [OWSTableItem actionWithTitle:@"Send text/x-signal-plain" - actionBlock:^{ - [DebugUITableViewController sendOversizeTextMessage:thread]; - }], - [OWSTableItem actionWithTitle:@"Send unknown mimetype" - actionBlock:^{ - [DebugUITableViewController sendRandomAttachment:thread - uti:SignalAttachment.kUnknownTestAttachmentUTI]; - }], - [OWSTableItem actionWithTitle:@"Send pdf" - actionBlock:^{ - [DebugUITableViewController sendRandomAttachment:thread - uti:(NSString *) kUTTypePDF]; - }], - ]]]; + + [contents + addSection:[OWSTableSection + sectionWithTitle:@"Messages View" + items:@[ + [OWSTableItem itemWithTitle:@"Send 10 messages (1/sec.)" + actionBlock:^{ + [DebugUITableViewController sendTextMessage:10 thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Send 100 messages (1/sec.)" + actionBlock:^{ + [DebugUITableViewController sendTextMessage:100 + thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Send text/x-signal-plain" + actionBlock:^{ + [DebugUITableViewController sendOversizeTextMessage:thread]; + }], + [OWSTableItem + itemWithTitle:@"Send unknown mimetype" + actionBlock:^{ + [DebugUITableViewController + sendRandomAttachment:thread + uti:SignalAttachment.kUnknownTestAttachmentUTI]; + }], + [OWSTableItem itemWithTitle:@"Send pdf" + actionBlock:^{ + [DebugUITableViewController + sendRandomAttachment:thread + uti:(NSString *)kUTTypePDF]; + }], + ]]]; [contents addSection:[OWSTableSection sectionWithTitle:@"Print to Debug Log" - items:@[ [OWSTableItem actionWithTitle:@"Print all sessions" - actionBlock:^{ - [[TSStorageManager sharedManager] - printAllSessions]; - }] ]]]; + items:@[ [OWSTableItem itemWithTitle:@"Print all sessions" + actionBlock:^{ + [[TSStorageManager sharedManager] + printAllSessions]; + }] ]]]; DebugUITableViewController *viewController = [DebugUITableViewController new]; viewController.contents = contents; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h index e3c1e1e795..743ea82f19 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h @@ -1,13 +1,15 @@ -// Created by Michael Kirk on 9/21/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// +#import "OWSTableViewController.h" #import NS_ASSUME_NONNULL_BEGIN @class TSThread; -@interface OWSConversationSettingsTableViewController : UITableViewController +@interface OWSConversationSettingsTableViewController : OWSTableViewController - (void)configureWithThread:(TSThread *)thread; - (void)presentedModalWasDismissed; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 84db329916..750d72f0fc 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -3,10 +3,12 @@ // #import "OWSConversationSettingsTableViewController.h" +#import "BlockListUIUtils.h" #import "Environment.h" #import "FingerprintViewController.h" #import "NewGroupViewController.h" #import "OWSAvatarBuilder.h" +#import "OWSBlockingManager.h" #import "OWSContactsManager.h" #import "PhoneNumber.h" #import "ShowGroupMembersViewController.h" @@ -27,23 +29,6 @@ NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerSection) { - OWSConversationSettingsTableViewControllerSectionContact, - OWSConversationSettingsTableViewControllerSectionGroup -}; - -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerContactCellIndex) { - OWSConversationSettingsTableViewControllerCellIndexShowFingerprint, - OWSConversationSettingsTableViewControllerCellIndexToggleDisappearingMessages, - OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration -}; - -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerGroupCellIndex) { - OWSConversationSettingsTableViewControllerCellIndexUpdateGroup, - OWSConversationSettingsTableViewControllerCellIndexLeaveGroup, - OWSConversationSettingsTableViewControllerCellIndexSeeGroupMembers -}; - static NSString *const OWSConversationSettingsTableViewControllerSegueUpdateGroup = @"OWSConversationSettingsTableViewControllerSegueUpdateGroup"; static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupMembers = @@ -51,22 +36,20 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM @interface OWSConversationSettingsTableViewController () -@property (strong, nonatomic) IBOutlet UITableViewCell *verifyPrivacyCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *toggleDisappearingMessagesCell; -@property (strong, nonatomic) IBOutlet UILabel *toggleDisappearingMessagesTitleLabel; -@property (strong, nonatomic) IBOutlet UILabel *toggleDisappearingMessagesDescriptionLabel; -@property (strong, nonatomic) IBOutlet UISwitch *disappearingMessagesSwitch; -@property (strong, nonatomic) IBOutlet UITableViewCell *disappearingMessagesDurationCell; -@property (strong, nonatomic) IBOutlet UILabel *disappearingMessagesDurationLabel; -@property (strong, nonatomic) IBOutlet UISlider *disappearingMessagesDurationSlider; +@property (nonatomic) IBOutlet UITableViewCell *verifyPrivacyCell; +@property (nonatomic) IBOutlet UITableViewCell *blocklistStateCell; +@property (nonatomic) IBOutlet UITableViewCell *toggleDisappearingMessagesCell; +@property (nonatomic) IBOutlet UILabel *toggleDisappearingMessagesTitleLabel; +@property (nonatomic) IBOutlet UILabel *toggleDisappearingMessagesDescriptionLabel; +@property (nonatomic) IBOutlet UISwitch *disappearingMessagesSwitch; +@property (nonatomic) IBOutlet UITableViewCell *disappearingMessagesDurationCell; +@property (nonatomic) IBOutlet UILabel *disappearingMessagesDurationLabel; +@property (nonatomic) IBOutlet UISlider *disappearingMessagesDurationSlider; -@property (strong, nonatomic) IBOutlet UITableViewCell *updateGroupCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *leaveGroupCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *listGroupMembersCell; -@property (strong, nonatomic) IBOutlet UIImageView *avatar; -@property (strong, nonatomic) IBOutlet UILabel *nameLabel; -@property (strong, nonatomic) IBOutlet UILabel *signalIdLabel; -@property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *cellIcons; +@property (nonatomic) IBOutlet UIImageView *avatar; +@property (nonatomic) IBOutlet UILabel *nameLabel; +@property (nonatomic) IBOutlet UILabel *signalIdLabel; +@property (nonatomic) IBOutletCollection(UIImageView) NSArray *cellIcons; @property (nonatomic) TSThread *thread; @property (nonatomic) NSString *contactName; @@ -80,6 +63,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; +@property (nonatomic, readonly) OWSBlockingManager *blockingManager; @end @@ -125,6 +109,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM _storageManager = [TSStorageManager sharedManager]; _contactsManager = [Environment getCurrent].contactsManager; _messageSender = [Environment getCurrent].messageSender; + _blockingManager = [OWSBlockingManager sharedManager]; } - (void)configureWithThread:(TSThread *)thread @@ -145,6 +130,15 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM #pragma mark - View Lifecycle +- (void)loadView +{ + // Initialize with empty contents. We'll populate the + // contents later. + self.contents = [OWSTableContents new]; + + [super loadView]; +} + - (void)viewDidLoad { [super viewDidLoad]; @@ -162,21 +156,10 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM // Translations self.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen"); - self.verifyPrivacyCell.textLabel.text - = NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings"); self.toggleDisappearingMessagesTitleLabel.text = NSLocalizedString(@"DISAPPEARING_MESSAGES", @"table cell label in conversation settings"); self.toggleDisappearingMessagesDescriptionLabel.text = NSLocalizedString(@"DISAPPEARING_MESSAGES_DESCRIPTION", @"subheading in conversation settings"); - self.updateGroupCell.textLabel.text - = NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings"); - self.leaveGroupCell.textLabel.text - = NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings"); - self.listGroupMembersCell.textLabel.text - = NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings"); - - self.toggleDisappearingMessagesCell.selectionStyle = UITableViewCellSelectionStyleNone; - self.disappearingMessagesDurationCell.selectionStyle = UITableViewCellSelectionStyleNone; self.disappearingMessagesDurations = [OWSDisappearingMessagesConfiguration validDurationsSeconds]; self.disappearingMessagesDurationSlider.maximumValue = (float)(self.disappearingMessagesDurations.count - 1); @@ -200,6 +183,131 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM for (UIImageView *cellIcon in self.cellIcons) { [cellIcon tintColorDidChange]; } + + [self updateTableContents]; +} + +- (void)updateTableContents +{ + OWSTableContents *contents = [OWSTableContents new]; + contents.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen"); + + __weak OWSConversationSettingsTableViewController *weakSelf = self; + + NSMutableArray *firstSectionItems = [NSMutableArray new]; + if (!self.isGroupThread && self.thread.hasSafetyNumbers) { + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.verifyPrivacyCell.textLabel.text + = NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings"); + return weakSelf.verifyPrivacyCell; + } + actionBlock:^{ + [weakSelf + performSegueWithIdentifier: + @"OWSConversationSettingsTableViewControllerSegueSafetyNumbers" + sender:weakSelf]; + }]]; + } + + if (!self.isGroupThread) { + BOOL isBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.signalId]; + + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text = NSLocalizedString( + @"CONVERSATION_SETTINGS_BLOCK_THIS_USER", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + UIImage *icon = [UIImage imageNamed:@"ic_block"]; + OWSAssert(icon); + cell.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + cell.imageView.contentMode = UIViewContentModeScaleToFill; + cell.imageView.tintColor = [UIColor blackColor]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + UISwitch *blockUserSwitch = [UISwitch new]; + blockUserSwitch.on = isBlocked; + [blockUserSwitch addTarget:self + action:@selector(blockUserSwitchDidChange:) + forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = blockUserSwitch; + return cell; + } + actionBlock:nil]]; + } + + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.toggleDisappearingMessagesCell.selectionStyle = UITableViewCellSelectionStyleNone; + return weakSelf.toggleDisappearingMessagesCell; + } + customRowHeight:108.f + actionBlock:nil]]; + + if (self.disappearingMessagesSwitch.isOn) { + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.disappearingMessagesDurationCell.selectionStyle = UITableViewCellSelectionStyleNone; + return weakSelf.disappearingMessagesDurationCell; + } + customRowHeight:76.f + actionBlock:nil]]; + } + + [contents addSection:[OWSTableSection sectionWithTitle:nil items:firstSectionItems]]; + + if (self.isGroupThread) { + NSArray *groupItems = @[ + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf performSegueWithIdentifier:@"OWSConversationSettingsTableViewControllerSegueUpdateGroup" + sender:weakSelf]; + }], + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf didTapLeaveGroup]; + }], + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf + performSegueWithIdentifier:@"OWSConversationSettingsTableViewControllerSegueShowGroupMembers" + sender:weakSelf]; + }], + ]; + + [contents addSection:[OWSTableSection sectionWithTitle:NSLocalizedString(@"GROUP_MANAGEMENT_SECTION", + @"Conversation settings table section title") + items:groupItems]]; + } + + self.contents = contents; + [self.tableView reloadData]; } - (void)viewWillAppear:(BOOL)animated @@ -244,105 +352,6 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM self.avatar.layer.cornerRadius = self.avatar.frame.size.height / 2.0f; } -#pragma mark - UITableViewDelegate - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - NSInteger baseCount = [super tableView:tableView numberOfRowsInSection:section]; - - if (section == OWSConversationSettingsTableViewControllerSectionGroup) { - if (self.isGroupThread) { - return baseCount; - } else { - return 0; - } - } - - if (section == OWSConversationSettingsTableViewControllerSectionContact) { - if (!self.thread.hasSafetyNumbers) { - baseCount -= 1; - } - - if (!self.disappearingMessagesSwitch.isOn) { - // hide duration slider when disappearing messages is off. - baseCount -= 1; - } - } - return baseCount; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath -{ - if (indexPath.section == OWSConversationSettingsTableViewControllerSectionContact - && !self.thread.hasSafetyNumbers) { - - // Since fingerprint cell is hidden for some threads we offset our index path - NSIndexPath *offsetIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section]; - return [super tableView:tableView cellForRowAtIndexPath:offsetIndexPath]; - } - - return [super tableView:tableView cellForRowAtIndexPath:indexPath]; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath]; - - // group vs. contact thread have some cells slider at different index. - if (cell == self.disappearingMessagesDurationCell) { - NSIndexPath *originalIndexPath = [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - - return [super tableView:tableView heightForRowAtIndexPath:originalIndexPath]; - } - if (cell == self.toggleDisappearingMessagesCell) { - NSIndexPath *originalIndexPath = - [NSIndexPath indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexToggleDisappearingMessages - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - - return [super tableView:tableView heightForRowAtIndexPath:originalIndexPath]; - } else { - return [super tableView:tableView heightForRowAtIndexPath:indexPath]; - } -} - -// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection. -- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - - // Don't highlight rows that have no selection style. - if (cell.selectionStyle == UITableViewCellSelectionStyleNone) { - return nil; - } - return indexPath; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - DDLogDebug(@"%@ tapped indexPath:%@", self.tag, indexPath); - - if (indexPath.section == OWSConversationSettingsTableViewControllerSectionGroup - && indexPath.row == OWSConversationSettingsTableViewControllerCellIndexLeaveGroup) { - - [self didTapLeaveGroup]; - } -} - -- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - if (section == OWSConversationSettingsTableViewControllerSectionGroup) { - if (self.isGroupThread) { - return NSLocalizedString(@"GROUP_MANAGEMENT_SECTION", @"Conversation settings table section title"); - } else { - return nil; - } - } else { - return [super tableView:tableView titleForHeaderInSection:section]; - } -} - #pragma mark - Actions - (void)didTapLeaveGroup @@ -407,6 +416,48 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM } UISwitch *disappearingMessagesSwitch = (UISwitch *)sender; [self toggleDisappearingMessages:disappearingMessagesSwitch.isOn]; + + [self updateTableContents]; +} + +- (void)blockUserSwitchDidChange:(id)sender +{ + OWSAssert(!self.isGroupThread); + + if (![sender isKindOfClass:[UISwitch class]]) { + DDLogError(@"%@ Unexpected sender for block user switch: %@", self.tag, sender); + } + UISwitch *blockUserSwitch = (UISwitch *)sender; + + BOOL isCurrentlyBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.signalId]; + + if (blockUserSwitch.isOn) { + OWSAssert(!isCurrentlyBlocked); + if (isCurrentlyBlocked) { + return; + } + [BlockListUIUtils showBlockPhoneNumberActionSheet:self.thread.contactIdentifier + displayName:self.thread.name + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + // Update switch state if user cancels action. + blockUserSwitch.on = isBlocked; + }]; + } else { + OWSAssert(isCurrentlyBlocked); + if (!isCurrentlyBlocked) { + return; + } + [BlockListUIUtils showUnblockPhoneNumberActionSheet:self.thread.contactIdentifier + displayName:self.thread.name + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + // Update switch state if user cancels action. + blockUserSwitch.on = isBlocked; + }]; + } } - (void)toggleDisappearingMessages:(BOOL)flag @@ -417,28 +468,6 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM // but it allows us to resuse the method to set the switch programmatically in view setup. self.disappearingMessagesSwitch.on = flag; [self durationSliderDidChange:self.disappearingMessagesDurationSlider]; - - // Animate show/hide of duration settings. - if (flag) { - [self.tableView insertRowsAtIndexPaths:@[ self.indexPathForDurationSlider ] - withRowAnimation:UITableViewRowAnimationFade]; - } else { - [self.tableView deleteRowsAtIndexPaths:@[ self.indexPathForDurationSlider ] - withRowAnimation:UITableViewRowAnimationFade]; - } -} - -- (NSIndexPath *)indexPathForDurationSlider -{ - if (!self.thread.hasSafetyNumbers) { - return [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - 1 - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - } else { - return [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - } } - (IBAction)durationSliderDidChange:(UISlider *)slider diff --git a/Signal/src/ViewControllers/OWSTableViewController.h b/Signal/src/ViewControllers/OWSTableViewController.h index 860c220159..2fb2b47a04 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.h +++ b/Signal/src/ViewControllers/OWSTableViewController.h @@ -32,15 +32,30 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - typedef NS_ENUM(NSInteger, OWSTableItemType) { + OWSTableItemTypeDefault, OWSTableItemTypeAction, }; typedef void (^OWSTableActionBlock)(); +typedef UITableViewCell * (^OWSTableCustomCellBlock)(); @interface OWSTableItem : NSObject -+ (OWSTableItem *)actionWithTitle:(NSString *)title - actionBlock:(OWSTableActionBlock)actionBlock; ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + actionBlock:(OWSTableActionBlock)actionBlock; + +- (UITableViewCell *)customCell; +- (NSNumber *)customRowHeight; @end diff --git a/Signal/src/ViewControllers/OWSTableViewController.m b/Signal/src/ViewControllers/OWSTableViewController.m index d556a88441..7c66ca72f4 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.m +++ b/Signal/src/ViewControllers/OWSTableViewController.m @@ -78,13 +78,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) NSString *title; @property (nonatomic, nullable) OWSTableActionBlock actionBlock; +@property (nonatomic) OWSTableCustomCellBlock customCellBlock; +@property (nonatomic) UITableViewCell *customCell; +@property (nonatomic) NSNumber *customRowHeight; + @end #pragma mark - @implementation OWSTableItem -+ (OWSTableItem *)actionWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock { OWSAssert(title.length > 0); @@ -95,6 +99,60 @@ NS_ASSUME_NONNULL_BEGIN return item; } ++ (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCell); + OWSAssert(customRowHeight > 0); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCell = customCell; + item.customRowHeight = @(customRowHeight); + return item; +} + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCellBlock); + OWSAssert(customRowHeight > 0); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCellBlock = customCellBlock; + item.customRowHeight = @(customRowHeight); + return item; +} + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCellBlock); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCellBlock = customCellBlock; + return item; +} + +- (UITableViewCell *)customCell +{ + if (_customCell) { + return _customCell; + } + if (_customCellBlock) { + return _customCellBlock(); + } + OWSAssert(0); + return nil; +} + @end #pragma mark - @@ -173,6 +231,12 @@ NSString * const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { OWSTableItem *item = [self itemForIndexPath:indexPath]; + + UITableViewCell *customCell = [item customCell]; + if (customCell) { + return customCell; + } + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kOWSTableCellIdentifier]; OWSAssert(cell); @@ -181,13 +245,32 @@ NSString * const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; return cell; } +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + OWSTableItem *item = [self itemForIndexPath:indexPath]; + if (item.customRowHeight) { + return [item.customRowHeight floatValue]; + } + return [super tableView:tableView heightForRowAtIndexPath:indexPath]; +} + +// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection. +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + OWSTableItem *item = [self itemForIndexPath:indexPath]; + if (!item.actionBlock) { + return nil; + } + + return indexPath; +} + - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; OWSTableItem *item = [self itemForIndexPath:indexPath]; - if (item.itemType == OWSTableItemTypeAction) { - OWSAssert(item.actionBlock); + if (item.actionBlock) { item.actionBlock(); } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b496d7a7fb..c1ca8d5cbe 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -100,23 +100,29 @@ /* Button label for the 'block' button */ "BLOCK_LIST_BLOCK_BUTTON" = "Block"; -/* A format for the 'block phone number' action sheet title. */ +/* A format for the 'block user' action sheet title. */ "BLOCK_LIST_BLOCK_TITLE_FORMAT" = "Block %@?"; /* Button label for the 'unblock' button */ "BLOCK_LIST_UNBLOCK_BUTTON" = "Unblock"; -/* A format for the 'unblock phone number' action sheet title. */ +/* A format for the 'unblock user' action sheet title. */ "BLOCK_LIST_UNBLOCK_TITLE_FORMAT" = "Unblock %@?"; /* A label for the block button in the block list view */ "BLOCK_LIST_VIEW_BLOCK_BUTTON" = "Block"; -/* The message format of the 'contact blocked' alert in the block view. It is populated with the blocked contact's name. */ -"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; +/* The title of the 'block user failed' alert. */ +"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_MESSAGE" = "Block user failed."; -/* The title of the 'contact blocked' alert in the block view. */ -"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_TITLE" = "Contact Blocked"; +/* The title of the 'block user failed' alert. */ +"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_TITLE" = "Error"; + +/* The message format of the 'user blocked' alert. It is populated with the blocked contact's name. */ +"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; + +/* The title of the 'user blocked' alert. */ +"BLOCK_LIST_VIEW_BLOCKED_ALERT_TITLE" = "User Blocked"; /* A title for the contacts section of the blocklist view. */ "BLOCK_LIST_VIEW_CONTACTS_SECTION_TITLE" = "Contacts"; @@ -127,11 +133,17 @@ /* The title of the 'phone number blocked' alert in the block view. */ "BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_TITLE" = "Phone Number Blocked"; -/* The message format of the 'phone number unblocked' alert in the block view. It is populated with the blocked phone number. */ +/* The title of the 'unblock user failed' alert. */ +"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_MESSAGE" = "Unblock user failed."; + +/* The title of the 'unblock user failed' alert. */ +"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_TITLE" = "Error"; + +/* The message format of the 'user unblocked' alert. It is populated with the blocked phone number. */ "BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been unblocked."; -/* The title of the 'phone number unblocked' alert in the block view. */ -"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE" = "Phone Number Unblocked"; +/* The title of the 'user unblocked' alert. */ +"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE" = "User Unblocked"; /* Accessibilty label for placing call button */ "CALL_LABEL" = "Call"; @@ -193,6 +205,9 @@ /* title for conversation settings screen */ "CONVERSATION_SETTINGS" = "Conversation Settings"; +/* table cell label in conversation settings */ +"CONVERSATION_SETTINGS_BLOCK_THIS_USER" = "Block this user"; + /* The message of the 'text message too large' alert. */ "CONVERSATION_VIEW_TEXT_MESSAGE_TOO_LARGE_ALERT_MESSAGE" = "This message is too long to send."; From dcb7eef3fcb7cffaa6ee33475330df3cec8764d5 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 4 Apr 2017 10:19:47 -0400 Subject: [PATCH 2/4] Respond to CR. // FREEBIE --- .../AddToBlockListViewController.m | 11 +++- Signal/src/ViewControllers/BlockListUIUtils.h | 6 +- Signal/src/ViewControllers/BlockListUIUtils.m | 26 +++++---- .../ViewControllers/BlockListViewController.m | 34 +---------- ...SConversationSettingsTableViewController.m | 4 +- Signal/src/contact/OWSContactsManager.h | 1 + Signal/src/contact/OWSContactsManager.m | 58 ++++++++++++++++--- 7 files changed, 84 insertions(+), 56 deletions(-) diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index b557277430..557cff5bbc 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -276,13 +276,14 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI __weak AddToBlockListViewController *weakSelf = self; [BlockListUIUtils showBlockPhoneNumberActionSheet:[parsedPhoneNumber toE164] - displayName:[parsedPhoneNumber toE164] fromViewController:self blockingManager:_blockingManager + contactsManager:_contactsManager completionBlock:^(BOOL isBlocked) { if (isBlocked) { // Clear phone number text field is block succeeds. weakSelf.phoneNumberTextField.text = nil; + [weakSelf.navigationController popViewControllerAnimated:YES]; } }]; } @@ -409,11 +410,17 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI { [tableView deselectRowAtIndexPath:indexPath animated:YES]; + __weak AddToBlockListViewController *weakSelf = self; Contact *contact = self.contacts[(NSUInteger)indexPath.item]; [BlockListUIUtils showBlockContactActionSheet:contact fromViewController:self blockingManager:_blockingManager - completionBlock:nil]; + contactsManager:_contactsManager + completionBlock:^(BOOL isBlocked) { + if (isBlocked) { + [weakSelf.navigationController popViewControllerAnimated:YES]; + } + }]; } #pragma mark - UIScrollViewDelegate diff --git a/Signal/src/ViewControllers/BlockListUIUtils.h b/Signal/src/ViewControllers/BlockListUIUtils.h index 23fb34975a..c26ca1c616 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.h +++ b/Signal/src/ViewControllers/BlockListUIUtils.h @@ -6,6 +6,7 @@ @class Contact; @class OWSBlockingManager; +@class OWSContactsManager; typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); @@ -16,18 +17,19 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); + (void)showBlockContactActionSheet:(Contact *)contact fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock; + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber - displayName:(NSString *)displayName fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock; + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber - displayName:(NSString *)displayName fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock; @end diff --git a/Signal/src/ViewControllers/BlockListUIUtils.m b/Signal/src/ViewControllers/BlockListUIUtils.m index 713cb0136b..1200aa0df2 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.m +++ b/Signal/src/ViewControllers/BlockListUIUtils.m @@ -3,6 +3,7 @@ // #import "BlockListUIUtils.h" +#import "OWSContactsManager.h" #import "PhoneNumber.h" #import #import @@ -14,6 +15,7 @@ NS_ASSUME_NONNULL_BEGIN + (void)showBlockContactActionSheet:(Contact *)contact fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock { NSMutableArray *phoneNumbers = [NSMutableArray new]; @@ -29,19 +31,21 @@ NS_ASSUME_NONNULL_BEGIN } return; } + NSString *displayName = [contactsManager displayNameForContact:contact]; [self showBlockPhoneNumbersActionSheet:phoneNumbers - displayName:contact.fullName + displayName:displayName fromViewController:fromViewController blockingManager:blockingManager completionBlock:completionBlock]; } + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber - displayName:(NSString *)displayName fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock { + NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; [self showBlockPhoneNumbersActionSheet:@[ phoneNumber ] displayName:displayName fromViewController:fromViewController @@ -61,7 +65,8 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(blockingManager); NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", - @"A format for the 'block user' action sheet title."), + @"A format for the 'block user' action sheet title. Embeds {{the " + @"blocked user's name or phone number}}."), displayName]; UIAlertController *actionSheetController = @@ -69,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN UIAlertAction *unblockAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") - style:UIAlertActionStyleDefault + style:UIAlertActionStyleDestructive handler:^(UIAlertAction *_Nonnull action) { [self blockPhoneNumbers:phoneNumbers displayName:displayName @@ -119,18 +124,20 @@ NS_ASSUME_NONNULL_BEGIN } + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber - displayName:(NSString *)displayName fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager completionBlock:(BlockActionCompletionBlock)completionBlock { OWSAssert(phoneNumber.length > 0); - OWSAssert(displayName.length > 0); OWSAssert(fromViewController); OWSAssert(blockingManager); + NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", - @"A format for the 'unblock user' action sheet title."), + @"A format for the 'unblock user' action sheet title. Embeds " + @"{{the blocked user's name or phone number}}."), displayName]; UIAlertController *actionSheetController = @@ -138,7 +145,7 @@ NS_ASSUME_NONNULL_BEGIN UIAlertAction *unblockAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") - style:UIAlertActionStyleDefault + style:UIAlertActionStyleDestructive handler:^(UIAlertAction *_Nonnull action) { [BlockListUIUtils unblockPhoneNumber:phoneNumber displayName:displayName @@ -179,8 +186,7 @@ NS_ASSUME_NONNULL_BEGIN message:[NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", @"The message format of the 'user unblocked' " - @"alert. It is populated with the " - @"blocked phone number."), + @"alert. Embeds {{the blocked user's name or phone number}}."), displayName] fromViewController:fromViewController]; } diff --git a/Signal/src/ViewControllers/BlockListViewController.m b/Signal/src/ViewControllers/BlockListViewController.m index 8732e09361..fd43f0e35c 100644 --- a/Signal/src/ViewControllers/BlockListViewController.m +++ b/Signal/src/ViewControllers/BlockListViewController.m @@ -20,7 +20,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic) NSArray *contacts; -@property (nonatomic) NSDictionary *contactMap; @end @@ -138,18 +137,7 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { - (NSString *)displayNameForIndexPath:(NSIndexPath *)indexPath { NSString *phoneNumber = _blockedPhoneNumbers[(NSUInteger)indexPath.item]; - PhoneNumber *parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:phoneNumber]; - - // Try to parse and present the phone number in E164. - // It should already be in E164, so this should always work. - // If an invalid or unparsable phone number is already in the block list, - // present it as-is. - NSString *displayName = (parsedPhoneNumber ? parsedPhoneNumber.toE164 : phoneNumber); - Contact *contact = self.contactMap[displayName]; - if (contact && [contact fullName].length > 0) { - displayName = [contact fullName]; - } - + NSString *displayName = [_contactsManager displayNameForPhoneIdentifier:phoneNumber]; return displayName; } @@ -168,11 +156,11 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { } case BlockListViewControllerSection_BlockList: { NSString *phoneNumber = _blockedPhoneNumbers[(NSUInteger)indexPath.item]; - NSString *displayName = [self displayNameForIndexPath:indexPath]; [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber - displayName:displayName fromViewController:self blockingManager:_blockingManager + + contactsManager:_contactsManager completionBlock:nil]; break; } @@ -203,22 +191,6 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { }); } -- (void)setContacts:(NSArray *)contacts -{ - _contacts = contacts; - - NSMutableDictionary *contactMap = [NSMutableDictionary new]; - for (Contact *contact in contacts) { - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - NSString *phoneNumberE164 = phoneNumber.toE164; - if (phoneNumberE164.length > 0) { - contactMap[phoneNumberE164] = contact; - } - } - } - self.contactMap = contactMap; -} - #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 750d72f0fc..1a5beb4892 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -437,9 +437,9 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM return; } [BlockListUIUtils showBlockPhoneNumberActionSheet:self.thread.contactIdentifier - displayName:self.thread.name fromViewController:self blockingManager:_blockingManager + contactsManager:_contactsManager completionBlock:^(BOOL isBlocked) { // Update switch state if user cancels action. blockUserSwitch.on = isBlocked; @@ -450,9 +450,9 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM return; } [BlockListUIUtils showUnblockPhoneNumberActionSheet:self.thread.contactIdentifier - displayName:self.thread.name fromViewController:self blockingManager:_blockingManager + contactsManager:_contactsManager completionBlock:^(BOOL isBlocked) { // Update switch state if user cancels action. blockUserSwitch.on = isBlocked; diff --git a/Signal/src/contact/OWSContactsManager.h b/Signal/src/contact/OWSContactsManager.h index a2564dbbcc..36cc0a5d82 100644 --- a/Signal/src/contact/OWSContactsManager.h +++ b/Signal/src/contact/OWSContactsManager.h @@ -39,6 +39,7 @@ extern NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification; - (void)doAfterEnvironmentInitSetup; - (NSString *)displayNameForPhoneIdentifier:(nullable NSString *)identifier; +- (NSString *)displayNameForContact:(Contact *)contact; - (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier; - (NSAttributedString *)formattedFullNameForContact:(Contact *)contact font:(UIFont *)font; diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index ce7c32f86f..93ccef4f09 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -20,12 +20,15 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification = @property TOCFuture *futureAddressBook; @property ObservableValueController *observableContactsController; @property TOCCancelTokenSource *life; -@property(atomic, copy) NSDictionary *latestContactsById; +@property (atomic) NSDictionary *latestContactsById; +@property (atomic) NSDictionary *contactMap; @end @implementation OWSContactsManager +@synthesize latestContactsById = _latestContactsById; + - (void)dealloc { [_life cancel]; } @@ -46,6 +49,41 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification = return self; } +- (NSDictionary *)latestContactsById +{ + @synchronized(self) + { + return _latestContactsById; + } +} + +- (void)setLatestContactsById:(NSDictionary *)latestContactsById +{ + @synchronized(self) + { + _latestContactsById = [latestContactsById copy]; + + NSMutableDictionary *contactMap = [NSMutableDictionary new]; + for (Contact *contact in _latestContactsById.allValues) { + // The allContacts method seems to protect against non-contact instances + // in latestContactsById, so I've done the same here. I'm not sure if + // this is a real issue. + OWSAssert([contact isKindOfClass:[Contact class]]); + if (![contact isKindOfClass:[Contact class]]) { + continue; + } + + for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { + NSString *phoneNumberE164 = phoneNumber.toE164; + if (phoneNumberE164.length > 0) { + contactMap[phoneNumberE164] = contact; + } + } + } + self.contactMap = contactMap; + } +} + - (void)doAfterEnvironmentInitSetup { if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) { self.contactStore = [[CNContactStore alloc] init]; @@ -419,6 +457,15 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in return displayName; } +- (NSString *_Nonnull)displayNameForContact:(Contact *)contact +{ + OWSAssert(contact); + + NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : self.unknownContactName; + + return displayName; +} + - (NSAttributedString *_Nonnull)formattedFullNameForContact:(Contact *)contact font:(UIFont *_Nonnull)font { UIFont *boldFont = [UIFont ows_mediumFontWithSize:font.pointSize]; @@ -473,14 +520,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in if (!identifier) { return nil; } - for (Contact *contact in self.allContacts) { - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - if ([phoneNumber.toE164 isEqualToString:identifier]) { - return contact; - } - } - } - return nil; + return self.contactMap[identifier]; } - (Contact *)getOrBuildContactForPhoneIdentifier:(NSString *)identifier From 253776d65c51a5275ab8fc220da16845dc3d5083 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 4 Apr 2017 10:26:14 -0400 Subject: [PATCH 3/4] Respond to CR. // FREEBIE --- .../translations/en.lproj/Localizable.strings | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index c1ca8d5cbe..b3ddf30775 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -100,13 +100,13 @@ /* Button label for the 'block' button */ "BLOCK_LIST_BLOCK_BUTTON" = "Block"; -/* A format for the 'block user' action sheet title. */ +/* A format for the 'block user' action sheet title. Embeds {{the blocked user's name or phone number}}. */ "BLOCK_LIST_BLOCK_TITLE_FORMAT" = "Block %@?"; /* Button label for the 'unblock' button */ "BLOCK_LIST_UNBLOCK_BUTTON" = "Unblock"; -/* A format for the 'unblock user' action sheet title. */ +/* A format for the 'unblock user' action sheet title. Embeds {{the blocked user's name or phone number}}. */ "BLOCK_LIST_UNBLOCK_TITLE_FORMAT" = "Unblock %@?"; /* A label for the block button in the block list view */ @@ -118,7 +118,7 @@ /* The title of the 'block user failed' alert. */ "BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_TITLE" = "Error"; -/* The message format of the 'user blocked' alert. It is populated with the blocked contact's name. */ +/* The message format of the 'user blocked' alert. Embeds {{the blocked user's name or phone number}}. */ "BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; /* The title of the 'user blocked' alert. */ @@ -127,19 +127,13 @@ /* A title for the contacts section of the blocklist view. */ "BLOCK_LIST_VIEW_CONTACTS_SECTION_TITLE" = "Contacts"; -/* The message format of the 'phone number blocked' alert in the block view. It is populated with the blocked phone number. */ -"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; - -/* The title of the 'phone number blocked' alert in the block view. */ -"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_TITLE" = "Phone Number Blocked"; - /* The title of the 'unblock user failed' alert. */ "BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_MESSAGE" = "Unblock user failed."; /* The title of the 'unblock user failed' alert. */ "BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_TITLE" = "Error"; -/* The message format of the 'user unblocked' alert. It is populated with the blocked phone number. */ +/* The message format of the 'user unblocked' alert. Embeds {{the blocked user's name or phone number}}. */ "BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been unblocked."; /* The title of the 'user unblocked' alert. */ @@ -748,10 +742,10 @@ /* Alert title */ "REGISTRATION_BODY" = "Failed to re-register for push notifications."; -/* No comment provided by engineer. */ +/* Label for the country code field */ "REGISTRATION_DEFAULT_COUNTRY_NAME" = "Country Code"; -/* No comment provided by engineer. */ +/* Placeholder text for the phone number textfield */ "REGISTRATION_ENTERNUMBER_DEFAULT_TEXT" = "Enter Number"; /* No comment provided by engineer. */ @@ -763,7 +757,7 @@ /* No comment provided by engineer. */ "REGISTRATION_NON_VALID_NUMBER" = "This phone number format is not supported, please contact support."; -/* No comment provided by engineer. */ +/* Label for the phone number textfield */ "REGISTRATION_PHONENUMBER_BUTTON" = "Phone Number"; /* No comment provided by engineer. */ @@ -856,7 +850,7 @@ /* A header title for the block list table. */ "SETTINGS_BLOCK_LIST_HEADER_TITLE" = "Blocked Phone Numbers"; -/* Label for the block list row of the settings view */ +/* Label for the block list section of the settings view */ "SETTINGS_BLOCK_LIST_TITLE" = "Blocked"; /* User settings section footer, a detailed explanation */ From 71007cc3d19f54c0e410d507ba770f8c8ab855aa Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 5 Apr 2017 09:09:09 -0400 Subject: [PATCH 4/4] Respond to CR. // FREEBIE --- Signal/src/Storyboard/Main.storyboard | 16 ++++---- .../AddToBlockListViewController.m | 2 +- Signal/src/ViewControllers/BlockListUIUtils.h | 10 +++-- Signal/src/ViewControllers/BlockListUIUtils.m | 40 +++++++++++++++---- .../ViewControllers/BlockListViewController.m | 1 - ...SConversationSettingsTableViewController.m | 1 + .../ViewControllers/OWSTableViewController.h | 8 ++-- .../ViewControllers/OWSTableViewController.m | 8 ++-- 8 files changed, 57 insertions(+), 29 deletions(-) diff --git a/Signal/src/Storyboard/Main.storyboard b/Signal/src/Storyboard/Main.storyboard index 2459c564ba..20cdd45987 100644 --- a/Signal/src/Storyboard/Main.storyboard +++ b/Signal/src/Storyboard/Main.storyboard @@ -479,7 +479,7 @@ - + @@ -535,13 +535,13 @@ - + @@ -550,7 +550,7 @@ - + @@ -566,7 +566,7 @@ - + diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index 557cff5bbc..27e25fd2e4 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -281,7 +281,7 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI contactsManager:_contactsManager completionBlock:^(BOOL isBlocked) { if (isBlocked) { - // Clear phone number text field is block succeeds. + // Clear phone number text field if block succeeds. weakSelf.phoneNumberTextField.text = nil; [weakSelf.navigationController popViewControllerAnimated:YES]; } diff --git a/Signal/src/ViewControllers/BlockListUIUtils.h b/Signal/src/ViewControllers/BlockListUIUtils.h index c26ca1c616..f1cf0671c8 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.h +++ b/Signal/src/ViewControllers/BlockListUIUtils.h @@ -4,6 +4,8 @@ #import +NS_ASSUME_NONNULL_BEGIN + @class Contact; @class OWSBlockingManager; @class OWSContactsManager; @@ -18,18 +20,20 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock; + completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock; + completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock; + completionBlock:(nullable BlockActionCompletionBlock)completionBlock; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/BlockListUIUtils.m b/Signal/src/ViewControllers/BlockListUIUtils.m index 1200aa0df2..1af210bf07 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.m +++ b/Signal/src/ViewControllers/BlockListUIUtils.m @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock + completionBlock:(nullable BlockActionCompletionBlock)completionBlock { NSMutableArray *phoneNumbers = [NSMutableArray new]; for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN } } if (phoneNumbers.count < 1) { + DDLogError(@"%@ Contact has no phone numbers", self.tag); + OWSAssert(0); [self showBlockFailedAlert:fromViewController]; if (completionBlock) { completionBlock(NO); @@ -43,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock + completionBlock:(nullable BlockActionCompletionBlock)completionBlock { NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; [self showBlockPhoneNumbersActionSheet:@[ phoneNumber ] @@ -57,7 +59,7 @@ NS_ASSUME_NONNULL_BEGIN displayName:(NSString *)displayName fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager - completionBlock:(BlockActionCompletionBlock)completionBlock + completionBlock:(nullable BlockActionCompletionBlock)completionBlock { OWSAssert(phoneNumbers.count > 0); OWSAssert(displayName.length > 0); @@ -67,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", @"A format for the 'block user' action sheet title. Embeds {{the " @"blocked user's name or phone number}}."), - displayName]; + [self formatDisplayNameForAlertTitle:displayName]]; UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -119,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT", @"The message format of the 'user blocked' " @"alert. Embeds {{the blocked user's name or phone number}}."), - displayName] + [self formatDisplayNameForAlertMessage:displayName]] fromViewController:fromViewController]; } @@ -127,18 +129,19 @@ NS_ASSUME_NONNULL_BEGIN fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager - completionBlock:(BlockActionCompletionBlock)completionBlock + completionBlock:(nullable BlockActionCompletionBlock)completionBlock { OWSAssert(phoneNumber.length > 0); OWSAssert(fromViewController); OWSAssert(blockingManager); + OWSAssert(contactsManager); NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", @"A format for the 'unblock user' action sheet title. Embeds " @"{{the blocked user's name or phone number}}."), - displayName]; + [self formatDisplayNameForAlertTitle:displayName]]; UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; @@ -187,7 +190,7 @@ NS_ASSUME_NONNULL_BEGIN stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", @"The message format of the 'user unblocked' " @"alert. Embeds {{the blocked user's name or phone number}}."), - displayName] + [self formatDisplayNameForAlertMessage:displayName]] fromViewController:fromViewController]; } @@ -230,6 +233,27 @@ NS_ASSUME_NONNULL_BEGIN [fromViewController presentViewController:controller animated:YES completion:nil]; } ++ (NSString *)formatDisplayNameForAlertTitle:(NSString *)displayName +{ + return [self formatDisplayName:displayName withMaxLength:20]; +} + ++ (NSString *)formatDisplayNameForAlertMessage:(NSString *)displayName +{ + return [self formatDisplayName:displayName withMaxLength:127]; +} + ++ (NSString *)formatDisplayName:(NSString *)displayName withMaxLength:(NSUInteger)maxLength +{ + OWSAssert(displayName.length > 0); + + if (displayName.length > maxLength) { + return [[displayName substringToIndex:maxLength] stringByAppendingString:@"…"]; + } + + return displayName; +} + #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/BlockListViewController.m b/Signal/src/ViewControllers/BlockListViewController.m index fd43f0e35c..c99be8edae 100644 --- a/Signal/src/ViewControllers/BlockListViewController.m +++ b/Signal/src/ViewControllers/BlockListViewController.m @@ -159,7 +159,6 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber fromViewController:self blockingManager:_blockingManager - contactsManager:_contactsManager completionBlock:nil]; break; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 1a5beb4892..4e20836dab 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -426,6 +426,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM if (![sender isKindOfClass:[UISwitch class]]) { DDLogError(@"%@ Unexpected sender for block user switch: %@", self.tag, sender); + OWSAssert(0); } UISwitch *blockUserSwitch = (UISwitch *)sender; diff --git a/Signal/src/ViewControllers/OWSTableViewController.h b/Signal/src/ViewControllers/OWSTableViewController.h index 2fb2b47a04..02c0e3a3fa 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.h +++ b/Signal/src/ViewControllers/OWSTableViewController.h @@ -41,18 +41,18 @@ typedef UITableViewCell * (^OWSTableCustomCellBlock)(); @interface OWSTableItem : NSObject -+ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock; ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(nullable OWSTableActionBlock)actionBlock; + (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell customRowHeight:(CGFloat)customRowHeight - actionBlock:(OWSTableActionBlock)actionBlock; + actionBlock:(nullable OWSTableActionBlock)actionBlock; + (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock customRowHeight:(CGFloat)customRowHeight - actionBlock:(OWSTableActionBlock)actionBlock; + actionBlock:(nullable OWSTableActionBlock)actionBlock; + (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock - actionBlock:(OWSTableActionBlock)actionBlock; + actionBlock:(nullable OWSTableActionBlock)actionBlock; - (UITableViewCell *)customCell; - (NSNumber *)customRowHeight; diff --git a/Signal/src/ViewControllers/OWSTableViewController.m b/Signal/src/ViewControllers/OWSTableViewController.m index 7c66ca72f4..c52de24e09 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.m +++ b/Signal/src/ViewControllers/OWSTableViewController.m @@ -88,7 +88,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSTableItem -+ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(nullable OWSTableActionBlock)actionBlock { OWSAssert(title.length > 0); @@ -101,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN + (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell customRowHeight:(CGFloat)customRowHeight - actionBlock:(OWSTableActionBlock)actionBlock + actionBlock:(nullable OWSTableActionBlock)actionBlock { OWSAssert(customCell); OWSAssert(customRowHeight > 0); @@ -116,7 +116,7 @@ NS_ASSUME_NONNULL_BEGIN + (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock customRowHeight:(CGFloat)customRowHeight - actionBlock:(OWSTableActionBlock)actionBlock + actionBlock:(nullable OWSTableActionBlock)actionBlock { OWSAssert(customCellBlock); OWSAssert(customRowHeight > 0); @@ -130,7 +130,7 @@ NS_ASSUME_NONNULL_BEGIN } + (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock - actionBlock:(OWSTableActionBlock)actionBlock + actionBlock:(nullable OWSTableActionBlock)actionBlock { OWSAssert(customCellBlock);