From 54cd8cfa3c3b6b7b7116f3d5fd0c7c333baa348a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 3 Apr 2017 10:29:11 -0400 Subject: [PATCH] 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 b7da912f8..4a133156a 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 000000000..9f5a8e00e --- /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 000000000..d3f232d57 --- /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 7c24dbcc5..2459c564b 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 ccc5fc369..b55727743 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 000000000..23fb34975 --- /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 000000000..713cb0136 --- /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 701a85549..8732e0936 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 5203bf91f..2af90b643 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 e3c1e1e79..743ea82f1 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 84db32991..750d72f0f 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 860c22015..2fb2b47a0 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 d556a8844..7c66ca72f 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 b496d7a7f..c1ca8d5cb 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.";