├── LeanCloudFeedback
├── resources
│ ├── feedback_btn@2x.png
│ ├── feedback_back@2x.png
│ ├── feedback_bg_1@2x.png
│ ├── feedback_bg_2@2x.png
│ ├── feedback_btn_new@2x.png
│ ├── feedback_warning@2x.png
│ ├── feedback_add_image@2x.png
│ └── LeanCloudFeedback.strings
├── LeanCloudFeedback.h
├── LCUserFeedbackImageViewController.h
├── LCUserFeedbackReply_Internal.h
├── LCUserFeedbackThread_Internal.h
├── LCUserFeedbackReplyCell.h
├── LCHttpClient.h
├── Info.plist
├── LCUserFeedbackImageViewController.m
├── LCUserFeedbackViewController.h
├── LCUserFeedbackReply.h
├── LCUserFeedbackThread.h
├── LCUserFeedbackAgent.h
├── LCUtils.h
├── LCUserFeedbackReply.m
├── LCUtils.m
├── LCUserFeedbackAgent.m
├── LCHttpClient.m
├── LCUserFeedbackThread.m
├── LCUserFeedbackReplyCell.m
└── LCUserFeedbackViewController.m
├── LeanCloudFeedbackDemo
├── LeanCloudFeedback.framework
│ └── Info.plist
├── LeanCloudFeedbackDemo
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── icon_29@2x.png
│ │ │ ├── icon_29@3x.png
│ │ │ ├── icon_40@2x.png
│ │ │ ├── icon_40@3x.png
│ │ │ ├── icon_60@2x.png
│ │ │ ├── icon_60@3x.png
│ │ │ └── Contents.json
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Info.plist
│ ├── ViewController.m
│ ├── AppDelegate.m
│ └── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
└── LeanCloudFeedbackDemo.xcodeproj
│ ├── project.xcworkspace
│ └── contents.xcworkspacedata
│ └── project.pbxproj
├── README.md
├── LeanCloudFeedback.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── LeanCloudFeedback.xcworkspace
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── contents.xcworkspacedata
├── Podfile.lock
├── Podfile
├── LeanCloudFeedback.podspec
└── .gitignore
/LeanCloudFeedback/resources/feedback_btn@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_btn@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_back@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_back@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_bg_1@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_bg_1@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_bg_2@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_bg_2@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_btn_new@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_btn_new@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_warning@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_warning@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/feedback_add_image@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedback/resources/feedback_add_image@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedback.framework/Info.plist:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedback.framework/Info.plist
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The user feedback API is deprecated.
2 | We recommend you [use LeanStorage][doc] to implement the feedback feature instead.
3 |
4 | [doc]: https://leancloud.cn/docs/rest_api.html#hash1473482921
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_29@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_29@3x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_40@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_40@3x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_60@2x.png
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/leancloud-feedback-ios/HEAD/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/icon_60@3x.png
--------------------------------------------------------------------------------
/LeanCloudFeedback.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LeanCloudFeedback.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // LeanCloudFeedbackDemo
4 | //
5 | // Created by Feng Junwen on 5/22/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/resources/LeanCloudFeedback.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | LeanCloudFeedback
4 |
5 | Created by lzw on 15/11/6.
6 | Copyright © 2015年 LeanCloud. All rights reserved.
7 | */
8 |
9 |
10 | "Detail"="详情";
11 | "Email Or QQ"="Email 或 QQ 号";
12 | "Please write your feedback"="请填写反馈";
13 | "Send"="发送";
14 | "User Feedback"="意见反馈";
15 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LeanCloudFeedback.h:
--------------------------------------------------------------------------------
1 | //
2 | // LeanCloudFeedback.h
3 | // LeanCloudFeedback
4 | //
5 | // Created by Feng Junwen on 5/18/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "LCUserFeedbackReply.h"
12 | #import "LCUserFeedbackAgent.h"
13 | #import "LCUserFeedbackThread.h"
14 | #import "LCUserFeedbackViewController.h"
15 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackImageViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackImageViewController.h
3 | // LeanCloudFeedback
4 | //
5 | // Created by lzw on 15/7/21.
6 | // Copyright (c) 2015年 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LCUserFeedbackImageViewController : UIViewController
12 |
13 | @property (nonatomic, strong) UIImage *image;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // LeanCloudFeedbackDemo
4 | //
5 | // Created by Feng Junwen on 5/22/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/LeanCloudFeedback.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackReply_Internal.h:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // LCUserFeedbackReply_Internal.h
4 | // LeanCloudFeedback
5 | //
6 | // Created by lzw on 15/7/21.
7 | // Copyright (c) 2015年 LeanCloud. All rights reserved.
8 | //
9 |
10 | #import "LCUserFeedbackReply.h"
11 |
12 | @interface LCUserFeedbackReply()
13 |
14 | @property(nonatomic, strong, readwrite) UIImage *attachmentImage;
15 | @property(nonatomic, copy, readwrite) NSString *createAt;
16 |
17 | @end
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // LeanCloudFeedbackDemo
4 | //
5 | // Created by Feng Junwen on 5/22/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackThread_Internal.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackThread_Internal.h
3 | // Feedback
4 | //
5 | // Created by Feng Junwen on 5/18/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUserFeedbackThread.h"
10 |
11 | @interface LCUserFeedbackThread ()
12 |
13 | +(void)feedbackWithContent:(NSString *)content
14 | contact:(NSString *)contact
15 | create:(BOOL)create
16 | withBlock:(AVIdResultBlock)block;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AVOSCloud (12.0.4)
3 | - LeanCloudFeedback (0.1.0):
4 | - AVOSCloud
5 |
6 | DEPENDENCIES:
7 | - AVOSCloud
8 | - LeanCloudFeedback (from `.`)
9 |
10 | SPEC REPOS:
11 | trunk:
12 | - AVOSCloud
13 |
14 | EXTERNAL SOURCES:
15 | LeanCloudFeedback:
16 | :path: "."
17 |
18 | SPEC CHECKSUMS:
19 | AVOSCloud: 6a3dd184e94a61caf3c8504e90172340a04798dd
20 | LeanCloudFeedback: 475a2cf99385a23cc7818116b81fb7c781b79ce0
21 |
22 | PODFILE CHECKSUM: b0677fc747183471f9d653b377e73db854e607f5
23 |
24 | COCOAPODS: 1.8.4
25 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '8.0'
3 | # Uncomment this line if you're using Swift
4 | # use_frameworks!
5 |
6 | platform :ios, '8.0'
7 |
8 | workspace 'LeanCloudFeedback.xcworkspace'
9 |
10 | target 'LeanCloudFeedback' do
11 | project 'LeanCloudFeedback.xcodeproj'
12 | pod 'AVOSCloud'
13 | end
14 |
15 | target 'LeanCloudFeedbackDemo' do
16 | project 'LeanCloudFeedbackDemo/LeanCloudFeedbackDemo.xcodeproj'
17 | pod 'AVOSCloud'
18 | pod 'LeanCloudFeedback', :path => '.'
19 | end
20 |
--------------------------------------------------------------------------------
/LeanCloudFeedback.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "LeanCloudFeedback"
4 | s.version = "0.1.4"
5 | s.platform = :ios, "8.0"
6 | s.summary = "LeanCloud iOS SDK for mobile backend."
7 | s.homepage = "https://leancloud.cn"
8 | s.documentation_url = "https://leancloud.cn/docs/feedback.html"
9 | s.license = {
10 | :type => "Commercial",
11 | :text => "Copyright 2015 LeanCloud, Inc. See https://leancloud.cn/terms.html"
12 | }
13 | s.author = { "LeanCloud" => "support@leancloud.cn" }
14 | s.source = { :git => "https://github.com/leancloud/leancloud-feedback-ios.git", :tag => s.version.to_s }
15 |
16 | s.source_files = "LeanCloudFeedback/**/*.{h,m}"
17 | s.public_header_files = "LeanCloudFeedback/**/*.h"
18 | s.resources = "LeanCloudFeedback/resources/*.{png,strings}"
19 |
20 | s.dependency 'AVOSCloud'
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackReplyCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackCell.h
3 | //
4 | // Created by yang chaozhong on 4/24/14.
5 | // Copyright (c) 2014 LeanCloud. All rights reserved.
6 | //
7 |
8 | #import
9 | #import "LCUserFeedbackReply.h"
10 |
11 | @protocol LCUserFeedbackReplyCellDelegate
12 |
13 | - (void)didSelectImageViewOnFeedbackReply:(LCUserFeedbackReply *)feedbackReply;
14 |
15 | @end
16 |
17 | @interface LCUserFeedbackReplyCell : UITableViewCell
18 |
19 | @property (nonatomic, strong) UIFont *cellFont UI_APPEARANCE_SELECTOR;
20 | @property (nonatomic, strong) id delegate;
21 |
22 | + (CGFloat)heightForFeedbackReply:(LCUserFeedbackReply *)reply;
23 |
24 | - (id)initWithFeedbackReply:(LCUserFeedbackReply *)reply reuseIdentifier:(NSString *)reuseIdentifier;
25 | - (void)configuareCellWithFeedbackReply:(LCUserFeedbackReply *)reply;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCHttpClient.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCHttpClient.h
3 | // Feedback
4 | //
5 | // Created by Feng Junwen on 5/15/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface LCHttpClient : NSObject
13 |
14 | +(LCHttpClient *)sharedInstance;
15 |
16 | -(void)postObject:(NSString *)path
17 | withParameters:(NSDictionary *)parameters
18 | block:(AVIdResultBlock)block;
19 |
20 | -(void)getObject:(NSString *)path
21 | withParameters:(NSDictionary *)parameters
22 | block:(AVIdResultBlock)block;
23 |
24 | -(void)putObject:(NSString *)path
25 | withParameters:(NSDictionary *)parameters
26 | block:(AVIdResultBlock)block;
27 |
28 | -(void)deleteObject:(NSString *)path
29 | withParameters:(NSDictionary *)parameters
30 | block:(AVIdResultBlock)block;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackImageViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackImageViewController.m
3 | // LeanCloudFeedback
4 | //
5 | // Created by lzw on 15/7/21.
6 | // Copyright (c) 2015年 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUserFeedbackImageViewController.h"
10 | #import "LCUtils.h"
11 |
12 | @interface LCUserFeedbackImageViewController ()
13 |
14 | @property (nonatomic, strong) UIImageView *imageView;
15 |
16 | @end
17 |
18 | @implementation LCUserFeedbackImageViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | self.title = LCLocalizedString(@"Detail");
23 | [self.view addSubview:self.imageView];
24 | }
25 |
26 | - (void)didReceiveMemoryWarning {
27 | [super didReceiveMemoryWarning];
28 | }
29 |
30 | - (UIImageView *)imageView {
31 | if (_imageView == nil) {
32 | _imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
33 | _imageView.contentMode = UIViewContentModeScaleAspectFill;
34 | _imageView.clipsToBounds = YES;
35 | _imageView.image = self.image;
36 | }
37 | return _imageView;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackViewController.h
3 | //
4 | // Created by yang chaozhong on 4/24/14.
5 | // Copyright (c) 2014 LeanCloud. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | typedef enum : NSUInteger {
11 | LCUserFeedbackNavigationBarStyleBlue = 0,
12 | LCUserFeedbackNavigationBarStyleNone,
13 | } LCUserFeedbackNavigationBarStyle;
14 |
15 | @interface LCUserFeedbackViewController : UIViewController
16 |
17 | /**
18 | * 导航栏主题,默认是蓝色主题
19 | */
20 | @property(nonatomic, assign) LCUserFeedbackNavigationBarStyle navigationBarStyle;
21 |
22 | /**
23 | * 是否隐藏联系方式表头, 默认不隐藏。假如不需要用户提供联系方式则可以隐藏。
24 | */
25 | @property(nonatomic, assign) BOOL contactHeaderHidden;
26 |
27 | /**
28 | * 设置字体。默认是大小为 16 的系统字体。
29 | */
30 | @property(nonatomic, strong) UIFont *feedbackCellFont;
31 |
32 | /**
33 | * 这个是网站看到的用户反馈标题。默认采用第一条反馈作为标题。
34 | */
35 | @property(nonatomic, copy) NSString *feedbackTitle;
36 |
37 | /**
38 | * 联系人。nil 需要用户自己填写。如果不需要用户填写联系方式,请设置 contactHeaderHidden
39 | */
40 | @property(nonatomic, copy) NSString *contact;
41 |
42 | /**
43 | * 是否使用 present 方式弹出。默认为YES,决定返回按钮样式和返回方式
44 | */
45 | @property(nonatomic, assign) BOOL presented;
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackReply.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackReply.h
3 | //
4 | // Created by yang chaozhong on 4/21/14.
5 | // Copyright (c) 2014 LeanCloud. All rights reserved.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | typedef enum : NSInteger {
12 | LCContentTypeText = 0 ,
13 | LCContentTypeImage
14 | }LCContentType;
15 |
16 | typedef enum : NSInteger {
17 | LCReplyTypeUser = 0,
18 | LCReplyTypeDev,
19 | }LCReplyType;
20 |
21 | @class LCUserFeedbackThread;
22 |
23 | @interface LCUserFeedbackReply : NSObject
24 |
25 | @property(nonatomic, retain) LCUserFeedbackThread *feedback;
26 |
27 | @property(nonatomic, copy, readonly) NSString *content;
28 | @property(nonatomic, copy, readonly) NSString *attachment;
29 | @property(nonatomic, strong, readonly) UIImage *attachmentImage;
30 | @property(nonatomic, assign, readonly) LCReplyType type;
31 | @property(nonatomic, copy, readonly) NSString *createAt;
32 |
33 | + (instancetype)feedbackReplyWithContent:(NSString *)content type:(LCReplyType)type;
34 | + (instancetype)feedbackReplyWithAttachment:(NSString *)attachment type:(LCReplyType)type;
35 |
36 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary;
37 |
38 | - (NSDictionary *)dictionary;
39 |
40 | - (LCContentType)contentType;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "icon_29@2x.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "icon_29@3x.png",
23 | "scale" : "3x"
24 | },
25 | {
26 | "size" : "40x40",
27 | "idiom" : "iphone",
28 | "filename" : "icon_40@2x.png",
29 | "scale" : "2x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "icon_40@3x.png",
35 | "scale" : "3x"
36 | },
37 | {
38 | "size" : "60x60",
39 | "idiom" : "iphone",
40 | "filename" : "icon_60@2x.png",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "icon_60@3x.png",
47 | "scale" : "3x"
48 | },
49 | {
50 | "idiom" : "ios-marketing",
51 | "size" : "1024x1024",
52 | "scale" : "1x"
53 | }
54 | ],
55 | "info" : {
56 | "version" : 1,
57 | "author" : "xcode"
58 | }
59 | }
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackThread.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackThread.h
3 | // Feedback
4 | //
5 | // Created by yang chaozhong on 4/21/14.
6 | // Copyright (c) 2014 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class LCUserFeedbackThread;
13 | @class LCUserFeedbackReply;
14 |
15 | typedef void (^LCUserFeedbackBlock)(LCUserFeedbackThread *feedback, NSError *error);
16 |
17 |
18 | @interface LCUserFeedbackThread : NSObject
19 |
20 | @property(nonatomic, copy) NSString *objectId;
21 | @property(nonatomic, copy) NSString *content;
22 | @property(nonatomic, copy) NSString *contact;
23 |
24 | +(void)fetchFeedbackWithBlock:(LCUserFeedbackBlock)block;
25 | +(void)fetchFeedbackWithContact:(NSString*)contact withBlock:(AVIdResultBlock)block AV_DEPRECATED("Use + fetchFeedbackWithBlock: instead");
26 | +(void)feedbackWithContent:(NSString *)content contact:(NSString *)contact withBlock:(AVIdResultBlock)block AV_DEPRECATED("Use + fetchFeedbackWithBlock: instead");
27 |
28 | +(void)updateFeedback:(LCUserFeedbackThread *)feedback withBlock:(AVIdResultBlock)block;
29 |
30 | +(void)deleteFeedback:(LCUserFeedbackThread *)feedback withBlock:(AVIdResultBlock)block;
31 |
32 | -(instancetype)initWithDictionary:(NSDictionary*)dict;
33 |
34 | -(void)saveFeedbackReplyInBackground:(LCUserFeedbackReply *)feedbackReply withBlock:(AVIdResultBlock)block;
35 |
36 | -(void)fetchFeedbackRepliesInBackgroundWithBlock:(AVArrayResultBlock)block;
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 |
37 | Pods/
38 |
39 | # Add this line if you want to avoid checking in source code from the Xcode workspace
40 | # *.xcworkspace
41 |
42 | # Carthage
43 | #
44 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
45 | # Carthage/Checkouts
46 |
47 | Carthage/Build
48 |
49 | # fastlane
50 | #
51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
52 | # screenshots whenever they are needed.
53 | # For more information about the recommended setup visit:
54 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
55 |
56 | fastlane/report.xml
57 | fastlane/Preview.html
58 | fastlane/screenshots/**/*.png
59 | fastlane/test_output
60 |
61 | # Code Injection
62 | #
63 | # After new code Injection tools there's a generated folder /iOSInjectionProject
64 | # https://github.com/johnno1962/injectionforxcode
65 |
66 | iOSInjectionProject/
67 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // LeanCloudFeedbackDemo
4 | //
5 | // Created by Feng Junwen on 5/22/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import
11 |
12 | @interface ViewController ()
13 |
14 | @property (weak, nonatomic) IBOutlet UILabel *unreadTipLabel;
15 |
16 | @end
17 |
18 | @implementation ViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | // Do any additional setup after loading the view, typically from a nib.
23 | self.unreadTipLabel.text = nil;
24 | [[LCUserFeedbackAgent sharedInstance] countUnreadFeedbackThreadsWithBlock:^(NSInteger number, NSError *error) {
25 | if (!error) {
26 | [self setUnreads:number];
27 | } else {
28 | [self setUnreads:0];
29 | }
30 | }];
31 | }
32 |
33 | - (void)setUnreads:(NSInteger)unreads {
34 | self.unreadTipLabel.text = [NSString stringWithFormat:@"您有 %ld 条未读反馈", unreads];
35 | }
36 |
37 | - (void)didReceiveMemoryWarning {
38 | [super didReceiveMemoryWarning];
39 | // Dispose of any resources that can be recreated.
40 | }
41 |
42 | - (IBAction)presentFeedbackView:(id)sender {
43 | LCUserFeedbackAgent *agent = [LCUserFeedbackAgent sharedInstance];
44 | // [agent showConversations:self title:nil contact:@"goodman@leancloud.cn"];
45 | [agent showConversations:self title:nil contact:nil];
46 |
47 | [self setUnreads:0];
48 | }
49 |
50 | - (IBAction)pushFeedbackView:(id)sender {
51 | LCUserFeedbackViewController *feedbackViewController = [[LCUserFeedbackViewController alloc] init];
52 | feedbackViewController.feedbackTitle = nil;
53 | feedbackViewController.contact = nil;
54 |
55 | // 隐藏联系人表头
56 | feedbackViewController.contactHeaderHidden = YES;
57 |
58 | // 决定返回按钮和样式
59 | feedbackViewController.presented = NO;
60 |
61 | // 不设置导航栏样式
62 | feedbackViewController.navigationBarStyle = LCUserFeedbackNavigationBarStyleNone;
63 |
64 | [self.navigationController pushViewController:feedbackViewController animated:YES];
65 |
66 | [self setUnreads:0];
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackAgent.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackAgent.h
3 | // Feedback
4 | //
5 | // Created by yang chaozhong on 4/22/14.
6 | // Copyright (c) 2014 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface LCUserFeedbackAgent : NSObject
13 |
14 | #pragma mark - methods
15 |
16 | /**
17 | * AVUserFeedbackAgent 实例
18 | */
19 | +(instancetype)sharedInstance;
20 |
21 | /**
22 | * 打开默认用户反馈界面
23 | * @param viewController 默认的用户反馈界面将会展示在 viewController 之上,可以设置为当前的 viewController,比如 self。
24 | * @param title 反馈标题,如果传 nil,默认将 thread 中的第一条反馈消息作为标题。
25 | * @param contact 联系方式,邮箱或qq。
26 | */
27 | - (void)showConversations:(UIViewController *)viewController title:(NSString *)title contact:(NSString *)contact;
28 |
29 | /**
30 | * 统计未读反馈数目
31 | *
32 | * @param block 结果回调。
33 | * @discussion 可用来设置小红点,提醒用户查看反馈
34 | */
35 | - (void)countUnreadFeedbackThreadsWithBlock:(AVIntegerResultBlock)block;
36 |
37 | /**
38 | * 从服务端同步反馈回复,按照 contact 同步。
39 | * @param title 该字段已废弃,应该传 nil。
40 | * @param contact 联系方式,邮箱或qq。
41 | * @param block 结果回调。
42 | * @discussion 可以在 block 中处理反馈数据 (AVUserFeedbackThread 数组),然后将其传入自定义用户反馈界面。
43 | */
44 | - (void)syncFeedbackThreadsWithBlock:(NSString *)title contact:(NSString *)contact block:(AVArrayResultBlock)block AV_DEPRECATED("Use -syncFeedbackThreadsWithContact:block: instead");
45 |
46 | /**
47 | * 从服务端同步反馈回复,按照 contact 同步。
48 | * @param contact 联系方式,邮箱或qq。
49 | * @param block 结果回调。
50 | * @discussion 可以在 block 中处理反馈数据 (AVUserFeedbackThread 数组),然后将其传入自定义用户反馈界面。
51 | */
52 | - (void)syncFeedbackThreadsWithContact:(NSString *)contact block:(AVArrayResultBlock)block AV_DEPRECATED("Because now feedback module is an open source project, you should tweak the UI in LCUserFeedbackViewController, do not make new UI and sync feedback threads by your own.");
53 |
54 | /**
55 | * 发送用户反馈
56 | * @param content 同上,用户反馈内容。
57 | * @param block 结果回调。
58 | */
59 | - (void)postFeedbackThread:(NSString *)content block:(AVIdResultBlock)block AV_DEPRECATED("Use -postFeedbackThread:contact:block: instead");
60 |
61 | /**
62 | * 发送用户反馈
63 | * @param content 同上,用户反馈内容。
64 | * @param contact 联系方式,邮箱或qq。
65 | * @param block 结果回调。
66 | */
67 | - (void)postFeedbackThread:(NSString *)content contact:(NSString *)contact block:(AVIdResultBlock)block AV_DEPRECATED("Because now feedback module is an open source project, you should tweak the UI in LCUserFeedbackViewController, do not make new UI and post feedback by your own.");
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCUtils.h
3 | // Feedback
4 | //
5 | // Created by Feng Junwen on 5/15/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #ifdef DEBUG
13 | # define FLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
14 | #else
15 | # define FLog(fmt, ...)
16 | #endif
17 |
18 | #define LCLocalizedString(name) NSLocalizedStringFromTableInBundle(name, @"LeanCloudFeedback", [NSBundle bundleForClass:[self class]], nil)
19 |
20 | #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
21 |
22 | @interface LCUtils : NSObject
23 |
24 | #pragma mark - Block
25 | + (void)callBooleanResultBlock:(AVBooleanResultBlock)block
26 | error:(NSError *)error;
27 |
28 | + (void)callIntegerResultBlock:(AVIntegerResultBlock)block
29 | number:(NSInteger)number
30 | error:(NSError *)error;
31 |
32 | + (void)callArrayResultBlock:(AVArrayResultBlock)block
33 | array:(NSArray *)array
34 | error:(NSError *)error;
35 |
36 | + (void)callObjectResultBlock:(AVObjectResultBlock)block
37 | object:(AVObject *)object
38 | error:(NSError *)error;
39 |
40 | + (void)callUserResultBlock:(AVUserResultBlock)block
41 | user:(AVUser *)user
42 | error:(NSError *)error;
43 |
44 | + (void)callIdResultBlock:(AVIdResultBlock)block
45 | object:(id)object
46 | error:(NSError *)error;
47 |
48 | + (void)callProgressBlock:(AVProgressBlock)block
49 | percent:(NSInteger)percentDone;
50 |
51 |
52 | + (void)callImageResultBlock:(AVImageResultBlock)block
53 | image:(UIImage *)image
54 | error:(NSError *)error;
55 |
56 | + (void)callFileResultBlock:(AVFileResultBlock)block
57 | AVFile:(AVFile *)file
58 | error:(NSError *)error;
59 |
60 | +(void)callSetResultBlock:(AVSetResultBlock)block
61 | set:(NSSet *)set
62 | error:(NSError *)error;
63 | +(void)callCloudQueryResultBlock:(AVCloudQueryCallback)block
64 | result:(AVCloudQueryResult *)result
65 | error:error;
66 |
67 | + (NSString*)calMD5:(NSString*)input;
68 |
69 | + (NSError *)errorWithText:(NSString *)format,... NS_FORMAT_FUNCTION(1, 2);
70 |
71 | @end
72 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackReply.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackReply.m
3 | //
4 | // Created by yang chaozhong on 4/21/14.
5 | // Copyright (c) 2014 LeanCloud. All rights reserved.
6 | //
7 |
8 | #import "LCUserFeedbackReply.h"
9 | #import "LCUserFeedbackReply_Internal.h"
10 | #import "LCUserFeedbackThread.h"
11 | #import "LCHttpClient.h"
12 | #import "LCUtils.h"
13 |
14 | @interface LCUserFeedbackReply ()
15 |
16 | @property(nonatomic, assign, readwrite) LCReplyType type;
17 | @property(nonatomic, copy, readwrite) NSString *attachment;
18 | @property(nonatomic, copy, readwrite) NSString *content;
19 |
20 | @end
21 |
22 | @implementation LCUserFeedbackReply
23 |
24 | - (NSDictionary *)dictionary {
25 | NSMutableDictionary * data = [[NSMutableDictionary alloc] init];
26 |
27 | if (self.type == LCReplyTypeDev) {
28 | [data setObject:@"dev" forKey:@"type"];
29 | } else {
30 | [data setObject:@"user" forKey:@"type"];
31 | }
32 | if (self.content) {
33 | [data setObject:self.content forKey:@"content"];
34 | }
35 | if (self.attachment) {
36 | [data setObject:self.attachment forKey:@"attachment"];
37 | }
38 | return [data copy];
39 | }
40 |
41 | + (instancetype)feedbackReplyWithContent:(NSString *)content type:(LCReplyType)type {
42 | LCUserFeedbackReply *feedbackReply = [[LCUserFeedbackReply alloc] init];
43 | feedbackReply.content = content;
44 | feedbackReply.type = type;
45 | return feedbackReply;
46 | }
47 |
48 | + (instancetype)feedbackReplyWithAttachment:(NSString *)attachment type:(LCReplyType)type {
49 | LCUserFeedbackReply *feedbackReply = [[LCUserFeedbackReply alloc] init];
50 | feedbackReply.attachment = attachment;
51 | feedbackReply.type = type;
52 | return feedbackReply;
53 | }
54 |
55 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
56 | LCUserFeedbackReply *feedbackReply = [[LCUserFeedbackReply alloc] init];
57 | feedbackReply.content = [dictionary objectForKey:@"content"];
58 | feedbackReply.createAt = [dictionary objectForKey:@"createdAt"];
59 | NSString *type = [dictionary objectForKey:@"type"];
60 | if ([type isEqualToString:@"user"]) {
61 | feedbackReply.type = LCReplyTypeUser;
62 | } else {
63 | feedbackReply.type = LCReplyTypeDev;
64 | }
65 | NSString *attachment = [dictionary objectForKey:@"attachment"];
66 | feedbackReply.attachment = attachment;
67 | return feedbackReply;
68 | }
69 |
70 | - (LCContentType)contentType {
71 | if (self.attachment) {
72 | return LCContentTypeImage;
73 | } else {
74 | return LCContentTypeText;
75 | }
76 | }
77 |
78 | @end
79 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // LeanCloudFeedbackDemo
4 | //
5 | // Created by Feng Junwen on 5/22/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import
11 |
12 | @interface AppDelegate ()
13 |
14 | @end
15 |
16 | @implementation AppDelegate
17 |
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 | // Override point for customization after application launch.
21 | #warning 用公共账号登录 https://leancloud.cn ,账号/密码:leancloud@163.com/Public123 即可查看用户反馈
22 | NSString *appId = @"sl9sxfb9d9x0sc5g8c5wsv00f4nvztrgo5qcx4i4sjk1myn3";
23 | NSString *appKey = @"ysz2l1zttl802m3qq8lvvqnmw8tnp4wiiuwe5xtydcjxrwtg";
24 | [AVOSCloud setApplicationId:appId clientKey:appKey serverURLString:@"https://sl9sxfb9.lc-cn-n1-shared.com"];
25 |
26 | [AVOSCloud setAllLogsEnabled:YES];
27 |
28 | return YES;
29 | }
30 |
31 | - (void)applicationWillResignActive:(UIApplication *)application {
32 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
33 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
34 | }
35 |
36 | - (void)applicationDidEnterBackground:(UIApplication *)application {
37 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
39 | }
40 |
41 | - (void)applicationWillEnterForeground:(UIApplication *)application {
42 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
43 | }
44 |
45 | - (void)applicationDidBecomeActive:(UIApplication *)application {
46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
47 | }
48 |
49 | - (void)applicationWillTerminate:(UIApplication *)application {
50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUtils.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUtils.m
3 | // Feedback
4 | //
5 | // Created by Feng Junwen on 5/15/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUtils.h"
10 | #import
11 |
12 | @implementation LCUtils
13 |
14 | #pragma mark - Safe way to call block
15 |
16 | #define safeBlock(first_param) \
17 | if (block) { \
18 | if ([NSThread isMainThread]) { \
19 | block(first_param, error); \
20 | }else {\
21 | dispatch_async(dispatch_get_main_queue(), ^{ \
22 | block(first_param, error); \
23 | }); \
24 | } \
25 | }
26 |
27 | + (void)callBooleanResultBlock:(AVBooleanResultBlock)block
28 | error:(NSError *)error
29 | {
30 | safeBlock(error == nil);
31 | }
32 |
33 | + (void)callIntegerResultBlock:(AVIntegerResultBlock)block
34 | number:(NSInteger)number
35 | error:(NSError *)error {
36 | safeBlock(number);
37 | }
38 |
39 | + (void)callArrayResultBlock:(AVArrayResultBlock)block
40 | array:(NSArray *)array
41 | error:(NSError *)error {
42 | safeBlock(array);
43 | }
44 |
45 | + (void)callObjectResultBlock:(AVObjectResultBlock)block
46 | object:(AVObject *)object
47 | error:(NSError *)error {
48 | safeBlock(object);
49 | }
50 |
51 | + (void)callUserResultBlock:(AVUserResultBlock)block
52 | user:(AVUser *)user
53 | error:(NSError *)error {
54 | safeBlock(user);
55 | }
56 |
57 | + (void)callIdResultBlock:(AVIdResultBlock)block
58 | object:(id)object
59 | error:(NSError *)error {
60 | safeBlock(object);
61 | }
62 |
63 | + (void)callImageResultBlock:(AVImageResultBlock)block
64 | image:(UIImage *)image
65 | error:(NSError *)error
66 | {
67 | safeBlock(image);
68 | }
69 |
70 | + (void)callFileResultBlock:(AVFileResultBlock)block
71 | AVFile:(AVFile *)file
72 | error:(NSError *)error
73 | {
74 | safeBlock(file);
75 | }
76 |
77 | + (void)callProgressBlock:(AVProgressBlock)block
78 | percent:(NSInteger)percentDone {
79 | if (block) {
80 | if ([NSThread isMainThread]) {
81 | block(percentDone);
82 | }else {
83 | dispatch_async(dispatch_get_main_queue(), ^{
84 | block(percentDone);
85 | });
86 | }
87 | }
88 | }
89 |
90 | +(void)callSetResultBlock:(AVSetResultBlock)block
91 | set:(NSSet *)set
92 | error:(NSError *)error
93 | {
94 | if (block) {
95 | if ([NSThread isMainThread]) {
96 | block(set, error);
97 | }else {
98 | dispatch_async(dispatch_get_main_queue(), ^{
99 | block(set, error);
100 | });
101 | }
102 | }
103 | }
104 |
105 | +(void)callCloudQueryResultBlock:(AVCloudQueryCallback)block
106 | result:(AVCloudQueryResult *)result
107 | error:error {
108 | safeBlock(result);
109 | }
110 |
111 | + (NSString*)calMD5:(NSString *)input {
112 | const char *cstr = [input UTF8String];
113 | unsigned char result[16];
114 | CC_MD5(cstr, (CC_LONG)strlen(cstr), result);
115 |
116 | return [[NSString stringWithFormat:
117 | @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
118 | result[0], result[1], result[2], result[3],
119 | result[4], result[5], result[6], result[7],
120 | result[8], result[9], result[10], result[11],
121 | result[12], result[13], result[14], result[15]
122 | ] lowercaseString];
123 | }
124 |
125 | + (NSError *)errorWithText:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2) {
126 | va_list ap;
127 | va_start(ap, format);
128 | NSDictionary *errorInfo = @{NSLocalizedDescriptionKey : [[NSString alloc] initWithFormat:format arguments:ap]};
129 | va_end(ap);
130 | NSError *error = [NSError errorWithDomain:@"LeanCloudFeedback Domain" code:0 userInfo:errorInfo];
131 | return error;
132 | }
133 |
134 | @end
135 |
136 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackAgent.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackAgent.m
3 | // Feedback
4 | //
5 | // Created by yang chaozhong on 4/22/14.
6 | // Copyright (c) 2014 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUserFeedbackAgent.h"
10 | #import "LCUserFeedbackThread.h"
11 | #import "LCUserFeedbackReply.h"
12 | #import "LCUserFeedbackViewController.h"
13 | #import "LCUtils.h"
14 |
15 | @interface LCUserFeedbackAgent()
16 |
17 | @property(nonatomic, retain) LCUserFeedbackThread *userFeedback;
18 |
19 | @end
20 |
21 | @implementation LCUserFeedbackAgent
22 |
23 | + (instancetype)sharedInstance {
24 | static dispatch_once_t once;
25 | static LCUserFeedbackAgent * sharedInstance;
26 | dispatch_once(&once, ^{
27 | sharedInstance = [[self alloc] init];
28 | });
29 | return sharedInstance;
30 | }
31 |
32 | - (void)showConversations:(UIViewController *)viewController title:(NSString *)title contact:(NSString *)contact{
33 | LCUserFeedbackViewController *feedbackViewController = [[LCUserFeedbackViewController alloc] init];
34 | feedbackViewController.feedbackTitle = title;
35 | feedbackViewController.contact = contact;
36 |
37 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:feedbackViewController];
38 | [viewController presentViewController:navigationController animated:YES completion:^{
39 | }];
40 | }
41 |
42 | - (void)syncFeedbackThreadsWithBlock:(NSString *)title contact:(NSString *)contact block:(AVArrayResultBlock)block {
43 | [self syncFeedbackThreadsWithContact:contact block:block];
44 | }
45 |
46 | - (void)syncFeedbackThreadsWithContact:(NSString *)contact block:(AVArrayResultBlock)block {
47 | [LCUserFeedbackThread fetchFeedbackWithContact:contact withBlock:^(id object, NSError *error) {
48 | if (!error) {
49 | if (object) {
50 | NSDictionary *dict = [[object objectForKey:@"results"] firstObject];
51 | self.userFeedback = [[LCUserFeedbackThread alloc] initWithDictionary:dict];
52 |
53 | [self.userFeedback fetchFeedbackRepliesInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
54 | [LCUtils callArrayResultBlock:block array:objects error:error];
55 | }];
56 | } else {
57 | [LCUtils callArrayResultBlock:block array:[NSArray array] error:nil];
58 | }
59 | } else {
60 | [LCUtils callIdResultBlock:block object:object error:error];
61 | }
62 | }];
63 | }
64 |
65 | - (void)postFeedbackThread:(NSString *)content block:(AVIdResultBlock)block {
66 | [self postFeedbackThread:content contact:nil block:block];
67 | }
68 |
69 | - (void)postFeedbackThread:(NSString *)content contact:(NSString *)contact block:(AVIdResultBlock)block {
70 | if ([_userFeedback objectId]) {
71 | LCUserFeedbackReply *feedbackReply = [LCUserFeedbackReply feedbackReplyWithContent:content type:LCReplyTypeUser];
72 | [self.userFeedback saveFeedbackReplyInBackground:feedbackReply withBlock:^(id object, NSError *error) {
73 | [LCUtils callIdResultBlock:block object:object error:error];
74 | }];
75 | } else {
76 | [LCUserFeedbackThread feedbackWithContent:content contact:contact withBlock:^(id object, NSError *error) {
77 | if (object && !error) {
78 | self.userFeedback = (LCUserFeedbackThread *)object;
79 | [self postFeedbackThread:content contact:contact block:block];
80 | } else {
81 | [LCUtils callIdResultBlock:block object:nil error:error];
82 | }
83 | }];
84 | }
85 | }
86 |
87 | - (void)countUnreadFeedbackThreadsWithBlock:(AVIntegerResultBlock)block {
88 | [LCUserFeedbackThread fetchFeedbackWithBlock:^(LCUserFeedbackThread *feedback, NSError *error) {
89 | if (error) {
90 | block(0, error);
91 | } else {
92 | if (feedback == nil) {
93 | block(0, nil);
94 | } else {
95 | NSString *localKey = [NSString stringWithFormat:@"feedback_%@", feedback.objectId];
96 | [feedback fetchFeedbackRepliesInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
97 | if (error) {
98 | block(0 ,error);
99 | } else {
100 | NSUInteger lastThreadsCounts = [[[NSUserDefaults standardUserDefaults] objectForKey:localKey] intValue];
101 | NSUInteger unreadCount = objects.count - lastThreadsCounts;
102 | block(unreadCount, nil);
103 | }
104 | }];
105 | }
106 | }
107 | }];
108 | }
109 |
110 | @end
111 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCHttpClient.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCHttpClient.m
3 | // Feedback
4 | //
5 | // Created by Feng Junwen on 5/15/15.
6 | // Copyright (c) 2015 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCHttpClient.h"
10 | #import "LCUtils.h"
11 | #import "LCRouter.h"
12 |
13 | @interface LCHttpClient ()
14 |
15 | @property (nonatomic, strong) NSURL *baseURL;
16 | @property (nonatomic, strong) NSOperationQueue *operationQueue;
17 |
18 | @end
19 |
20 | @implementation LCHttpClient
21 |
22 | + (LCHttpClient*)sharedInstance {
23 | static dispatch_once_t once;
24 | static LCHttpClient * sharedInstance;
25 | dispatch_once(&once, ^{
26 | sharedInstance = [[self alloc] init];
27 | sharedInstance.baseURL = [NSURL URLWithString:@"https://api.leancloud.cn/1.1/"];
28 | sharedInstance.operationQueue = [[NSOperationQueue alloc] init];
29 | });
30 | return sharedInstance;
31 | }
32 |
33 | - (NSString *)queryStringFromParameters:(NSDictionary *)parameters {
34 | NSMutableString *queries = [[NSMutableString alloc] init];
35 | NSArray *keys = [parameters allKeys];
36 | for (int i = 0; i < keys.count; i++) {
37 | if (i != 0) {
38 | [queries appendString:@"&"];
39 | }
40 | NSString *value = [parameters valueForKey:keys[i]];
41 | [queries appendFormat:@"%@=%@", keys[i], value];
42 | }
43 | return [queries stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
44 | }
45 |
46 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
47 |
48 | NSURL *url = [NSURL URLWithString:path];
49 |
50 | if (!url.scheme.length) {
51 | #pragma clang diagnostic push
52 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
53 | NSString *URLString = [[LCRouter sharedInstance] URLStringForPath:path];
54 | #pragma clang diagnostic pop
55 | url = [NSURL URLWithString:URLString];
56 | }
57 |
58 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
59 | [request setValue:[AVOSCloud getApplicationId] forHTTPHeaderField:@"X-LC-Id"];
60 |
61 | NSString *timestamp=[NSString stringWithFormat:@"%.0f",1000*[[NSDate date] timeIntervalSince1970]];
62 | NSString *sign=[LCUtils calMD5:[NSString stringWithFormat:@"%@%@",timestamp,[AVOSCloud getClientKey]]];
63 | NSString *headerValue=[NSString stringWithFormat:@"%@,%@",sign,timestamp];
64 | [request setValue:headerValue forHTTPHeaderField:@"X-LC-Sign"];
65 | [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
66 |
67 | if ([AVUser currentUser].sessionToken) {
68 | [request setValue:[AVUser currentUser].sessionToken forHTTPHeaderField:@"X-LC-Session"];
69 | }
70 |
71 | [request setHTTPMethod:method];
72 | if ([method isEqualToString:@"GET"] || [method isEqualToString:@"DELETE"]) {
73 | url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"?%@", [self queryStringFromParameters:parameters]]];
74 | [request setURL:url];
75 | } else {
76 | [request setValue:[NSString stringWithFormat:@"application/json"] forHTTPHeaderField:@"Content-Type"];
77 | NSError *error;
78 | [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error]];
79 | if (error) {
80 | FLog(@"%@ error : %@", [self class], error);
81 | }
82 | }
83 | return request;
84 | }
85 |
86 | - (void)goRequest:(NSURLRequest *)request block:(AVIdResultBlock)block {
87 | FLog(@"request url : %@", request.URL);
88 | FLog(@"request headers : %@", [request allHTTPHeaderFields]);
89 | FLog(@"request body %@", [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]);
90 | [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
91 | if (connectionError) {
92 | block(nil, connectionError);
93 | } else {
94 | if (response && data) {
95 | NSError *error;
96 | NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
97 | if (error) {
98 | NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
99 | FLog(@"reponse : %@", responseString);
100 | block(nil, [LCUtils errorWithText:@"Http request failed, reponse string : %@", responseString]);
101 | } else {
102 | block(dictionary, nil);
103 | }
104 | }
105 | }
106 | }];
107 | }
108 |
109 | - (NSDictionary *)whereDictionaryFromConditions:(NSDictionary *)conditions {
110 | NSDictionary *where = [NSDictionary dictionary];
111 | if (conditions.count>0) {
112 | NSData *data = [NSJSONSerialization dataWithJSONObject:conditions options:0 error:nil];
113 | NSString *conditionString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
114 | where = @{@"where":conditionString};
115 | }
116 | return where;
117 | }
118 |
119 | -(void)getObject:(NSString *)path
120 | withParameters:(NSDictionary *)parameters
121 | block:(AVIdResultBlock)block {
122 | NSMutableURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:[self whereDictionaryFromConditions:parameters]];
123 | [self goRequest:request block:block];
124 | }
125 |
126 | -(void)postObject:(NSString *)path
127 | withParameters:(NSDictionary *)parameters
128 | block:(AVIdResultBlock)block
129 | {
130 | NSMutableURLRequest *request= [self requestWithMethod:@"POST" path:path parameters:parameters];
131 | [self goRequest:request block:block];
132 | }
133 |
134 | -(void)putObject:(NSString *)path
135 | withParameters:(NSDictionary *)parameters
136 | block:(AVIdResultBlock)block {
137 | NSMutableURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
138 | [self goRequest:request block:block];
139 | }
140 |
141 | -(void)deleteObject:(NSString *)path
142 | withParameters:(NSDictionary *)parameters
143 | block:(AVIdResultBlock)block {
144 | NSMutableURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
145 | [self goRequest:request block:block];
146 | }
147 |
148 | @end
149 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
45 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackThread.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackThread.m
3 | // Feedback
4 | //
5 | // Created by yang chaozhong on 4/21/14.
6 | // Copyright (c) 2014 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUserFeedbackThread.h"
10 | #import "LCUserFeedbackThread_Internal.h"
11 | #import "LCUserFeedbackReply.h"
12 | #import "LCUserFeedbackReply_Internal.h"
13 | #import "LCHttpClient.h"
14 | #import "LCUtils.h"
15 |
16 | static NSString *const kLCUserFeedbackObjectId = @"LCUserFeedbackObjectId";
17 |
18 | @interface LCUserFeedbackThread()
19 |
20 | @property(nonatomic, copy) NSString *appSign;
21 | @property(nonatomic, copy) NSString *status;
22 | @property(nonatomic, copy) NSString *remarks;
23 | @property(nonatomic, copy) NSString *iid;
24 |
25 | @end
26 |
27 | #define LC_FEEDBACK_BASE_PATH @"feedback"
28 |
29 | @implementation LCUserFeedbackThread
30 |
31 | + (NSString *)objectPath {
32 | return LC_FEEDBACK_BASE_PATH;
33 | }
34 |
35 | - (NSString *)threadsPath {
36 | return [NSString stringWithFormat:@"%@/%@/threads", LC_FEEDBACK_BASE_PATH, self.objectId];
37 | }
38 |
39 | -(NSMutableDictionary *)postData {
40 | NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
41 |
42 | if (_content) {
43 | [data setObject:self.content forKey:@"content"];
44 | }
45 |
46 | if (_contact) {
47 | [data setObject:self.contact forKey:@"contact"];
48 | }
49 |
50 | if (_status) {
51 | [data setObject:self.status forKey:@"status"];
52 | }
53 |
54 | if (_remarks) {
55 | [data setObject:self.remarks forKey:@"remarks"];
56 | }
57 | _iid = [AVInstallation defaultInstallation].objectId;
58 | if (_iid) {
59 | [data setObject:self.iid forKey:@"iid"];
60 | }
61 |
62 | return data;
63 | }
64 |
65 | -(instancetype)initWithDictionary:(NSDictionary*)dict {
66 | self = [super init];
67 | if (self && dict) {
68 | self.contact = [dict objectForKey:@"contact"];
69 | self.content = [dict objectForKey:@"content"];
70 | self.status = [dict objectForKey:@"status"];
71 | self.iid = [dict objectForKey:@"iid"];
72 | self.remarks = [dict objectForKey:@"remarks"];
73 | self.objectId = [dict objectForKey:@"objectId"];
74 | }
75 | return self;
76 | }
77 |
78 | +(void)fetchFeedbackWithBlock:(LCUserFeedbackBlock)block {
79 | NSString *feedbackObjectId = [[NSUserDefaults standardUserDefaults] objectForKey:kLCUserFeedbackObjectId];
80 | if (feedbackObjectId == nil) {
81 | // do not create empty feedback
82 | block(nil, nil);
83 | } else {
84 | LCHttpClient *client = [LCHttpClient sharedInstance];
85 | NSString *path = [[LCUserFeedbackThread objectPath] stringByAppendingPathComponent:feedbackObjectId];
86 | [client getObject:path withParameters:nil block:^(id object, NSError *error) {
87 | if (error) {
88 | [LCUtils callIdResultBlock:block object:nil error:error];
89 | } else {
90 | if (object) {
91 | LCUserFeedbackThread *feedback = [[LCUserFeedbackThread alloc] initWithDictionary:object];
92 | [LCUtils callIdResultBlock:block object:feedback error:nil];
93 | } else {
94 | [LCUtils callIdResultBlock:block object:nil error:nil];
95 | }
96 | }
97 | }];
98 | }
99 | }
100 |
101 | +(void)fetchFeedbackWithContact:(NSString*)contact withBlock:(AVIdResultBlock)block {
102 | LCHttpClient *client = [LCHttpClient sharedInstance];
103 | [client getObject:[LCUserFeedbackThread objectPath] withParameters:[NSDictionary dictionaryWithObject:contact forKey:@"contact"] block:^(id object, NSError *error) {
104 | if (error) {
105 | [LCUtils callIdResultBlock:block object:nil error:error];
106 | } else {
107 | [LCUtils callIdResultBlock:block object:object error:nil];
108 | }
109 | }];
110 | }
111 |
112 | +(void)feedbackWithContent:(NSString *)content
113 | contact:(NSString *)contact
114 | create:(BOOL)create
115 | withBlock:(AVIdResultBlock)block {
116 | if (create) {
117 | LCUserFeedbackThread *feedback = [[LCUserFeedbackThread alloc] init];
118 | feedback.content = content;
119 | feedback.contact = contact;
120 |
121 | LCHttpClient *client = [LCHttpClient sharedInstance];
122 | [client postObject:[LCUserFeedbackThread objectPath] withParameters:[feedback postData] block:^(id object, NSError *error) {
123 | if (!error) {
124 | feedback.objectId = [(NSDictionary *)object objectForKey:@"objectId"];
125 | [[NSUserDefaults standardUserDefaults] setObject:feedback.objectId forKey:kLCUserFeedbackObjectId];
126 | [LCUtils callIdResultBlock:block object:feedback error:error];
127 | } else {
128 | [LCUtils callIdResultBlock:block object:nil error:error];
129 | }
130 | }];
131 | } else {
132 | [LCUtils callIdResultBlock:block object:nil error:[NSError new]];
133 | }
134 | }
135 |
136 | +(void)feedbackWithContent:(NSString *)content
137 | contact:(NSString *)contact
138 | withBlock:(AVIdResultBlock)block {
139 | [self feedbackWithContent:content contact:contact create:YES withBlock:block];
140 | }
141 |
142 |
143 | +(void)updateFeedback:(LCUserFeedbackThread *)feedback withBlock:(AVIdResultBlock)block {
144 | [[LCHttpClient sharedInstance] putObject:[NSString stringWithFormat:@"%@/%@", [LCUserFeedbackThread objectPath], feedback.objectId]
145 | withParameters:[feedback postData]
146 | block:^(id object, NSError *error) {
147 | [LCUtils callIdResultBlock:block object:object error:error];
148 | }];
149 | }
150 |
151 | +(void)deleteFeedback:(LCUserFeedbackThread *)feedback withBlock:(AVIdResultBlock)block {
152 | [[LCHttpClient sharedInstance] deleteObject:[NSString stringWithFormat:@"%@/%@", [LCUserFeedbackThread objectPath], feedback.objectId]
153 | withParameters:nil
154 | block:^(id object, NSError *error) {
155 | [LCUtils callIdResultBlock:block object:object error:error];
156 | }];
157 | }
158 |
159 | - (NSDictionary *)parametersWithFeedbackReply:(LCUserFeedbackReply *)feedbackReply {
160 | NSMutableDictionary *parameters = [[feedbackReply dictionary] mutableCopy];
161 |
162 | parameters[@"feedback"] = self.objectId;
163 |
164 | return [parameters copy];
165 | }
166 |
167 | - (void)saveFeedbackReplyInBackground:(LCUserFeedbackReply *)feedbackReply withBlock:(AVIdResultBlock)block {
168 | if (!self.objectId) return;
169 | [[LCHttpClient sharedInstance] postObject:[self threadsPath] withParameters:[self parametersWithFeedbackReply:feedbackReply] block:^(id object, NSError *error){
170 | if (!error) {
171 | feedbackReply.createAt = [object objectForKey:@"createdAt"];
172 | }
173 | [LCUtils callIdResultBlock:block object:object error:error];
174 | }];
175 | }
176 |
177 | - (void)fetchFeedbackRepliesInBackgroundWithBlock:(AVArrayResultBlock)block {
178 | [[LCHttpClient sharedInstance] getObject:[self threadsPath]
179 | withParameters:nil
180 | block:^(id object, NSError *error)
181 | {
182 | NSArray *results = [object objectForKey:@"results"];
183 | NSMutableArray *replies = [NSMutableArray arrayWithCapacity:[results count]];
184 |
185 | for (NSDictionary *dictionary in results) {
186 | [replies addObject:[[LCUserFeedbackReply alloc] initWithDictionary:dictionary]];
187 | }
188 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
189 | for (LCUserFeedbackReply *reply in replies) {
190 | if (reply.attachment) {
191 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:reply.attachment]];
192 | if (data) {
193 | reply.attachmentImage = [UIImage imageWithData:data];
194 | } else {
195 | FLog(@"attachment getData error");
196 | }
197 | }
198 | }
199 | dispatch_async(dispatch_get_main_queue(), ^{
200 | [LCUtils callArrayResultBlock:block array:replies error:error];
201 | });
202 | });
203 | }];
204 | }
205 |
206 | @end
207 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackReplyCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackCell.m
3 | //
4 | // Created by yang chaozhong on 4/24/14.
5 | // Copyright (c) 2014 LeanCloud. All rights reserved.
6 | //
7 |
8 | #import "LCUserFeedbackReplyCell.h"
9 | #import "LCUserFeedbackReply.h"
10 |
11 | static CGFloat const kLabelVerticalPadding = 10;
12 | static CGFloat const kTimestampLabelHeight = 18;
13 | static CGFloat const kMaxiumCellWidth = 226;
14 | static CGFloat const kBubbleHorizontalPadding = 10;
15 | static CGFloat const kContentHorizontalPadding = 10;
16 | static CGFloat const kContentVerticalPadding = 10;
17 |
18 | static CGFloat const kBubbleArrowHeight = 14; /**< 气泡尖嘴的高度 */
19 |
20 | @interface LCUserFeedbackReplyCell()
21 |
22 | @property (nonatomic, strong) UILabel *timestampLabel;
23 |
24 | @property (nonatomic, strong) UIImageView *bubbleImageView;
25 |
26 | @property (nonatomic, strong) LCUserFeedbackReply *feedbackReply;
27 |
28 | @end
29 |
30 | @implementation LCUserFeedbackReplyCell
31 |
32 | #pragma mark - life cycle
33 |
34 | - (id)initWithFeedbackReply:(LCUserFeedbackReply *)reply reuseIdentifier:(NSString *)reuseIdentifier {
35 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
36 | if (self) {
37 | [self setupWithFeedbackReply:reply];
38 | }
39 | return self;
40 | }
41 |
42 | - (void)setupWithFeedbackReply:(LCUserFeedbackReply *)reply {
43 | self.backgroundColor = [UIColor clearColor];
44 | [self setupTextLabel];
45 | [self setupImageView];
46 | [self.contentView addSubview:self.timestampLabel];
47 | switch (reply.contentType) {
48 | case LCContentTypeText:
49 | [self.contentView insertSubview:self.bubbleImageView belowSubview:self.textLabel];
50 | break;
51 | case LCContentTypeImage:
52 | [self.contentView insertSubview:self.bubbleImageView belowSubview:self.imageView];
53 | break;
54 | }
55 | self.selectionStyle = UITableViewCellSelectionStyleNone;
56 | }
57 |
58 | #pragma mark - property
59 |
60 | - (void)setupTextLabel {
61 | if ([[UIDevice currentDevice].systemVersion floatValue] < 7.0f) {
62 | self.textLabel.backgroundColor = [UIColor whiteColor];
63 | }
64 | self.textLabel.font = [[LCUserFeedbackReplyCell appearance] cellFont];
65 | self.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
66 | self.textLabel.numberOfLines = 0;
67 | self.textLabel.textAlignment = NSTextAlignmentLeft;
68 | self.textLabel.textColor = [UIColor blackColor];
69 | }
70 |
71 | - (void)setupImageView {
72 | self.imageView.contentMode = UIViewContentModeScaleAspectFill;
73 | self.imageView.clipsToBounds = YES;
74 | self.imageView.userInteractionEnabled = YES;
75 | }
76 |
77 | - (UILabel *)timestampLabel {
78 | if (_timestampLabel == nil) {
79 | _timestampLabel = [[UILabel alloc] init];
80 | _timestampLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
81 | _timestampLabel.textAlignment = NSTextAlignmentCenter;
82 | _timestampLabel.backgroundColor = [UIColor clearColor];
83 | _timestampLabel.font = [UIFont systemFontOfSize:9.0f];
84 | _timestampLabel.textColor = [UIColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1.0];
85 | _timestampLabel.frame = CGRectMake(0.0f, kLabelVerticalPadding, self.bounds.size.width, kTimestampLabelHeight);
86 | }
87 | return _timestampLabel;
88 | }
89 |
90 | - (UIImageView *)bubbleImageView {
91 | if (_bubbleImageView == nil) {
92 | _bubbleImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
93 | }
94 | return _bubbleImageView;
95 | }
96 |
97 | #pragma mark - size or frame
98 |
99 | + (CGSize)getBubbleSizeWithReply:(LCUserFeedbackReply *)reply {
100 | CGSize bubbleSize;
101 | switch (reply.contentType) {
102 | case LCContentTypeText: {
103 | bubbleSize = [self caculateLabelSizeForText:reply.content];
104 | break;
105 | }
106 | case LCContentTypeImage:
107 | bubbleSize = CGSizeMake(120, 120);
108 | break;
109 | }
110 | bubbleSize = CGSizeMake(bubbleSize.width + kContentHorizontalPadding * 2, bubbleSize.height + kContentVerticalPadding * 2);
111 | bubbleSize.height += kBubbleArrowHeight;
112 | return bubbleSize;
113 | }
114 |
115 | + (CGFloat)heightForFeedbackReply:(LCUserFeedbackReply *)reply {
116 | CGFloat height = kTimestampLabelHeight + 2 * kLabelVerticalPadding;
117 | CGSize bubbleSize = [self getBubbleSizeWithReply:reply];
118 | height += bubbleSize.height;
119 | return height;
120 | }
121 |
122 | - (CGRect)bubbleFrame {
123 | CGSize bubbleSize = [[self class] getBubbleSizeWithReply:self.feedbackReply];
124 |
125 | CGFloat paddingX = kBubbleHorizontalPadding;
126 | if (self.feedbackReply.type == LCReplyTypeUser) {
127 | paddingX = CGRectGetWidth(self.bounds) - bubbleSize.width - kBubbleHorizontalPadding;
128 | }
129 | CGFloat originY = CGRectGetMaxY(self.timestampLabel.frame) + kLabelVerticalPadding;
130 | return CGRectMake(paddingX, originY, bubbleSize.width, bubbleSize.height);
131 | }
132 | /**
133 | * 气泡去除掉尖嘴的 frame
134 | */
135 | - (CGRect)bubbleContentFrame {
136 | CGRect contentFrame = [self bubbleFrame];
137 | switch (self.feedbackReply.type) {
138 | case LCReplyTypeDev: {
139 | // 尖嘴在上方
140 | contentFrame.origin.y += kBubbleArrowHeight;
141 | contentFrame.size.height -= kBubbleArrowHeight;
142 | }
143 | break;
144 | case LCReplyTypeUser:
145 | // 尖嘴在下方
146 | contentFrame.size.height -= kBubbleArrowHeight;
147 | break;
148 | }
149 | return contentFrame;
150 | }
151 |
152 | #pragma mark - configuare
153 |
154 | - (void)configuareCellWithFeedbackReply:(LCUserFeedbackReply *)reply {
155 | for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
156 | [self.imageView removeGestureRecognizer:recognizer];
157 | }
158 |
159 | self.feedbackReply = reply;
160 | self.bubbleImageView.image = [self backgroundImageForFeedbackReply:reply];
161 | self.timestampLabel.text = [self formatDateString:reply.createAt];
162 | switch (reply.contentType) {
163 | case LCContentTypeText:
164 | self.textLabel.text = reply.content;
165 | break;
166 | case LCContentTypeImage:
167 | self.imageView.image = reply.attachmentImage;
168 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGestureRecognizer:)];
169 | tap.delegate = self;
170 | tap.cancelsTouchesInView = NO;
171 | [self.imageView addGestureRecognizer:tap];
172 | break;
173 | }
174 | }
175 |
176 | - (void)layoutSubviews {
177 | [super layoutSubviews];
178 |
179 | CGRect bubbleFrame = [self bubbleFrame];
180 | self.bubbleImageView.frame = bubbleFrame;
181 | CGRect contentFrame = [self bubbleContentFrame];
182 | CGRect innerContentFrame = CGRectMake(CGRectGetMinX(contentFrame) + kContentHorizontalPadding, CGRectGetMinY(contentFrame) + kContentVerticalPadding, CGRectGetWidth(contentFrame) - kContentHorizontalPadding * 2, CGRectGetHeight(contentFrame) - kContentVerticalPadding * 2);
183 | switch (self.feedbackReply.contentType) {
184 | case LCContentTypeText: {
185 | self.textLabel.frame = innerContentFrame;
186 | break;
187 | }
188 | case LCContentTypeImage: {
189 | self.imageView.frame = innerContentFrame;
190 | break;
191 | }
192 | }
193 | }
194 |
195 | - (void)prepareForReuse {
196 | [super prepareForReuse];
197 | self.timestampLabel.text = nil;
198 | self.textLabel.text = nil;
199 | self.imageView.image = nil;
200 | self.bubbleImageView.image = nil;
201 | }
202 |
203 | #pragma mark - action
204 |
205 | - (void)handleTapGestureRecognizer:(UITapGestureRecognizer *)tap {
206 | if (tap.state == UIGestureRecognizerStateEnded) {
207 | if ([self.delegate respondsToSelector:@selector(didSelectImageViewOnFeedbackReply:)]) {
208 | [self.delegate didSelectImageViewOnFeedbackReply:self.feedbackReply];
209 | }
210 | }
211 | }
212 |
213 | #pragma mark - utils
214 |
215 | + (CGSize)caculateLabelSizeForText:(NSString *)text {
216 | UILabel *gettingSizeLabel = [[UILabel alloc] init];
217 | gettingSizeLabel.font = [[LCUserFeedbackReplyCell appearance] cellFont];
218 | gettingSizeLabel.lineBreakMode = NSLineBreakByWordWrapping;
219 | gettingSizeLabel.numberOfLines = 0;
220 | gettingSizeLabel.text = text;
221 | CGSize maxiumLabelSize = CGSizeMake(kMaxiumCellWidth, MAXFLOAT);
222 | return [gettingSizeLabel sizeThatFits:maxiumLabelSize];
223 | }
224 |
225 | - (UIImage *)backgroundImageForFeedbackReply:(LCUserFeedbackReply *)reply {
226 | if (reply.type == LCReplyTypeDev) {
227 | return [[UIImage imageNamed:@"feedback_bg_1"] stretchableImageWithLeftCapWidth:20 topCapHeight:16];
228 | } else {
229 | return [[UIImage imageNamed:@"feedback_bg_2"] stretchableImageWithLeftCapWidth:1 topCapHeight:16];;
230 | }
231 | }
232 |
233 | - (NSString *)formatDateString:(NSString *)dateString {
234 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
235 | [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
236 | [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
237 | NSDate *date = [dateFormatter dateFromString:dateString];
238 |
239 | dateFormatter = [[NSDateFormatter alloc] init];
240 | [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm"];
241 | return [dateFormatter stringFromDate:date];
242 | }
243 |
244 | #pragma mark -
245 |
246 | - (void)awakeFromNib
247 | {
248 | [super awakeFromNib];
249 | // Initialization code
250 | }
251 |
252 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated
253 | {
254 | [super setSelected:selected animated:animated];
255 |
256 | // Configure the view for the selected state
257 | }
258 |
259 |
260 | @end
261 |
--------------------------------------------------------------------------------
/LeanCloudFeedbackDemo/LeanCloudFeedbackDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 5CC504E71B0F01D3004D0CB1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CC504E61B0F01D3004D0CB1 /* main.m */; };
11 | 5CC504EA1B0F01D3004D0CB1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CC504E91B0F01D3004D0CB1 /* AppDelegate.m */; };
12 | 5CC504ED1B0F01D3004D0CB1 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CC504EC1B0F01D3004D0CB1 /* ViewController.m */; };
13 | 5CC504F01B0F01D3004D0CB1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5CC504EE1B0F01D3004D0CB1 /* Main.storyboard */; };
14 | 5CC504F21B0F01D3004D0CB1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CC504F11B0F01D3004D0CB1 /* Images.xcassets */; };
15 | 840DDC8E9FF039EA6EE94011 /* libPods-LeanCloudFeedbackDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C033AA2C00DA993AA57FB14 /* libPods-LeanCloudFeedbackDemo.a */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXFileReference section */
19 | 3C033AA2C00DA993AA57FB14 /* libPods-LeanCloudFeedbackDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LeanCloudFeedbackDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
20 | 5CC504E11B0F01D3004D0CB1 /* LeanCloudFeedbackDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LeanCloudFeedbackDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 5CC504E51B0F01D3004D0CB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
22 | 5CC504E61B0F01D3004D0CB1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
23 | 5CC504E81B0F01D3004D0CB1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
24 | 5CC504E91B0F01D3004D0CB1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
25 | 5CC504EB1B0F01D3004D0CB1 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
26 | 5CC504EC1B0F01D3004D0CB1 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
27 | 5CC504EF1B0F01D3004D0CB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
28 | 5CC504F11B0F01D3004D0CB1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
29 | 5CC504F41B0F01D3004D0CB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
30 | 5CC504FF1B0F01D3004D0CB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | 5CC505001B0F01D3004D0CB1 /* LeanCloudFeedbackDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LeanCloudFeedbackDemoTests.m; sourceTree = ""; };
32 | 7465E688A3D8D6072420F996 /* Pods-LeanCloudFeedbackDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeanCloudFeedbackDemo.release.xcconfig"; path = "../Pods/Target Support Files/Pods-LeanCloudFeedbackDemo/Pods-LeanCloudFeedbackDemo.release.xcconfig"; sourceTree = ""; };
33 | C28BF2C6C8C5354952538019 /* Pods-LeanCloudFeedbackDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeanCloudFeedbackDemo.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-LeanCloudFeedbackDemo/Pods-LeanCloudFeedbackDemo.debug.xcconfig"; sourceTree = ""; };
34 | /* End PBXFileReference section */
35 |
36 | /* Begin PBXFrameworksBuildPhase section */
37 | 5CC504DE1B0F01D3004D0CB1 /* Frameworks */ = {
38 | isa = PBXFrameworksBuildPhase;
39 | buildActionMask = 2147483647;
40 | files = (
41 | 840DDC8E9FF039EA6EE94011 /* libPods-LeanCloudFeedbackDemo.a in Frameworks */,
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | 009A4221AB1D8BBE1A8638BD /* Pods */ = {
49 | isa = PBXGroup;
50 | children = (
51 | C28BF2C6C8C5354952538019 /* Pods-LeanCloudFeedbackDemo.debug.xcconfig */,
52 | 7465E688A3D8D6072420F996 /* Pods-LeanCloudFeedbackDemo.release.xcconfig */,
53 | );
54 | name = Pods;
55 | sourceTree = "";
56 | };
57 | 5CC504D81B0F01D3004D0CB1 = {
58 | isa = PBXGroup;
59 | children = (
60 | 5CC504E31B0F01D3004D0CB1 /* LeanCloudFeedbackDemo */,
61 | 5CC504FD1B0F01D3004D0CB1 /* LeanCloudFeedbackDemoTests */,
62 | 5CC504E21B0F01D3004D0CB1 /* Products */,
63 | 009A4221AB1D8BBE1A8638BD /* Pods */,
64 | 82A4E34BE877AC5780E8CC96 /* Frameworks */,
65 | );
66 | sourceTree = "";
67 | };
68 | 5CC504E21B0F01D3004D0CB1 /* Products */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 5CC504E11B0F01D3004D0CB1 /* LeanCloudFeedbackDemo.app */,
72 | );
73 | name = Products;
74 | sourceTree = "";
75 | };
76 | 5CC504E31B0F01D3004D0CB1 /* LeanCloudFeedbackDemo */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 5CC504E81B0F01D3004D0CB1 /* AppDelegate.h */,
80 | 5CC504E91B0F01D3004D0CB1 /* AppDelegate.m */,
81 | 5CC504EB1B0F01D3004D0CB1 /* ViewController.h */,
82 | 5CC504EC1B0F01D3004D0CB1 /* ViewController.m */,
83 | 5CC504EE1B0F01D3004D0CB1 /* Main.storyboard */,
84 | 5CC504F11B0F01D3004D0CB1 /* Images.xcassets */,
85 | 5CC504F31B0F01D3004D0CB1 /* LaunchScreen.xib */,
86 | 5CC504E41B0F01D3004D0CB1 /* Supporting Files */,
87 | );
88 | path = LeanCloudFeedbackDemo;
89 | sourceTree = "";
90 | };
91 | 5CC504E41B0F01D3004D0CB1 /* Supporting Files */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 5CC504E51B0F01D3004D0CB1 /* Info.plist */,
95 | 5CC504E61B0F01D3004D0CB1 /* main.m */,
96 | );
97 | name = "Supporting Files";
98 | sourceTree = "";
99 | };
100 | 5CC504FD1B0F01D3004D0CB1 /* LeanCloudFeedbackDemoTests */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 5CC505001B0F01D3004D0CB1 /* LeanCloudFeedbackDemoTests.m */,
104 | 5CC504FE1B0F01D3004D0CB1 /* Supporting Files */,
105 | );
106 | path = LeanCloudFeedbackDemoTests;
107 | sourceTree = "";
108 | };
109 | 5CC504FE1B0F01D3004D0CB1 /* Supporting Files */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 5CC504FF1B0F01D3004D0CB1 /* Info.plist */,
113 | );
114 | name = "Supporting Files";
115 | sourceTree = "";
116 | };
117 | 82A4E34BE877AC5780E8CC96 /* Frameworks */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 3C033AA2C00DA993AA57FB14 /* libPods-LeanCloudFeedbackDemo.a */,
121 | );
122 | name = Frameworks;
123 | sourceTree = "";
124 | };
125 | /* End PBXGroup section */
126 |
127 | /* Begin PBXNativeTarget section */
128 | 5CC504E01B0F01D3004D0CB1 /* LeanCloudFeedbackDemo */ = {
129 | isa = PBXNativeTarget;
130 | buildConfigurationList = 5CC505041B0F01D3004D0CB1 /* Build configuration list for PBXNativeTarget "LeanCloudFeedbackDemo" */;
131 | buildPhases = (
132 | 4769E60FE17A8611CF49B3CF /* [CP] Check Pods Manifest.lock */,
133 | 5CC504DD1B0F01D3004D0CB1 /* Sources */,
134 | 5CC504DE1B0F01D3004D0CB1 /* Frameworks */,
135 | 5CC504DF1B0F01D3004D0CB1 /* Resources */,
136 | 3BC15B31DE1EE218A416CB6C /* [CP] Copy Pods Resources */,
137 | );
138 | buildRules = (
139 | );
140 | dependencies = (
141 | );
142 | name = LeanCloudFeedbackDemo;
143 | productName = LeanCloudFeedbackDemo;
144 | productReference = 5CC504E11B0F01D3004D0CB1 /* LeanCloudFeedbackDemo.app */;
145 | productType = "com.apple.product-type.application";
146 | };
147 | /* End PBXNativeTarget section */
148 |
149 | /* Begin PBXProject section */
150 | 5CC504D91B0F01D3004D0CB1 /* Project object */ = {
151 | isa = PBXProject;
152 | attributes = {
153 | LastUpgradeCheck = 1120;
154 | ORGANIZATIONNAME = LeanCloud;
155 | TargetAttributes = {
156 | 5CC504E01B0F01D3004D0CB1 = {
157 | CreatedOnToolsVersion = 6.3.2;
158 | DevelopmentTeam = 7J5XFNL99Q;
159 | };
160 | };
161 | };
162 | buildConfigurationList = 5CC504DC1B0F01D3004D0CB1 /* Build configuration list for PBXProject "LeanCloudFeedbackDemo" */;
163 | compatibilityVersion = "Xcode 3.2";
164 | developmentRegion = English;
165 | hasScannedForEncodings = 0;
166 | knownRegions = (
167 | English,
168 | en,
169 | Base,
170 | );
171 | mainGroup = 5CC504D81B0F01D3004D0CB1;
172 | productRefGroup = 5CC504E21B0F01D3004D0CB1 /* Products */;
173 | projectDirPath = "";
174 | projectRoot = "";
175 | targets = (
176 | 5CC504E01B0F01D3004D0CB1 /* LeanCloudFeedbackDemo */,
177 | );
178 | };
179 | /* End PBXProject section */
180 |
181 | /* Begin PBXResourcesBuildPhase section */
182 | 5CC504DF1B0F01D3004D0CB1 /* Resources */ = {
183 | isa = PBXResourcesBuildPhase;
184 | buildActionMask = 2147483647;
185 | files = (
186 | 5CC504F01B0F01D3004D0CB1 /* Main.storyboard in Resources */,
187 | 5CC504F21B0F01D3004D0CB1 /* Images.xcassets in Resources */,
188 | );
189 | runOnlyForDeploymentPostprocessing = 0;
190 | };
191 | /* End PBXResourcesBuildPhase section */
192 |
193 | /* Begin PBXShellScriptBuildPhase section */
194 | 3BC15B31DE1EE218A416CB6C /* [CP] Copy Pods Resources */ = {
195 | isa = PBXShellScriptBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | );
199 | inputPaths = (
200 | "${PODS_ROOT}/Target Support Files/Pods-LeanCloudFeedbackDemo/Pods-LeanCloudFeedbackDemo-resources.sh",
201 | "${PODS_ROOT}/AVOSCloud/AVOS/AVOSCloud/AVOSCloud_Art.inc",
202 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_add_image@2x.png",
203 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_back@2x.png",
204 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_bg_1@2x.png",
205 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_bg_2@2x.png",
206 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_btn@2x.png",
207 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_btn_new@2x.png",
208 | "${PODS_ROOT}/../LeanCloudFeedback/resources/feedback_warning@2x.png",
209 | "${PODS_ROOT}/../LeanCloudFeedback/resources/LeanCloudFeedback.strings",
210 | );
211 | name = "[CP] Copy Pods Resources";
212 | outputPaths = (
213 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AVOSCloud_Art.inc",
214 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_add_image@2x.png",
215 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_back@2x.png",
216 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_bg_1@2x.png",
217 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_bg_2@2x.png",
218 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_btn@2x.png",
219 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_btn_new@2x.png",
220 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/feedback_warning@2x.png",
221 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LeanCloudFeedback.strings",
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | shellPath = /bin/sh;
225 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LeanCloudFeedbackDemo/Pods-LeanCloudFeedbackDemo-resources.sh\"\n";
226 | showEnvVarsInLog = 0;
227 | };
228 | 4769E60FE17A8611CF49B3CF /* [CP] Check Pods Manifest.lock */ = {
229 | isa = PBXShellScriptBuildPhase;
230 | buildActionMask = 2147483647;
231 | files = (
232 | );
233 | inputPaths = (
234 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
235 | "${PODS_ROOT}/Manifest.lock",
236 | );
237 | name = "[CP] Check Pods Manifest.lock";
238 | outputPaths = (
239 | "$(DERIVED_FILE_DIR)/Pods-LeanCloudFeedbackDemo-checkManifestLockResult.txt",
240 | );
241 | runOnlyForDeploymentPostprocessing = 0;
242 | shellPath = /bin/sh;
243 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
244 | showEnvVarsInLog = 0;
245 | };
246 | /* End PBXShellScriptBuildPhase section */
247 |
248 | /* Begin PBXSourcesBuildPhase section */
249 | 5CC504DD1B0F01D3004D0CB1 /* Sources */ = {
250 | isa = PBXSourcesBuildPhase;
251 | buildActionMask = 2147483647;
252 | files = (
253 | 5CC504ED1B0F01D3004D0CB1 /* ViewController.m in Sources */,
254 | 5CC504EA1B0F01D3004D0CB1 /* AppDelegate.m in Sources */,
255 | 5CC504E71B0F01D3004D0CB1 /* main.m in Sources */,
256 | );
257 | runOnlyForDeploymentPostprocessing = 0;
258 | };
259 | /* End PBXSourcesBuildPhase section */
260 |
261 | /* Begin PBXVariantGroup section */
262 | 5CC504EE1B0F01D3004D0CB1 /* Main.storyboard */ = {
263 | isa = PBXVariantGroup;
264 | children = (
265 | 5CC504EF1B0F01D3004D0CB1 /* Base */,
266 | );
267 | name = Main.storyboard;
268 | sourceTree = "";
269 | };
270 | 5CC504F31B0F01D3004D0CB1 /* LaunchScreen.xib */ = {
271 | isa = PBXVariantGroup;
272 | children = (
273 | 5CC504F41B0F01D3004D0CB1 /* Base */,
274 | );
275 | name = LaunchScreen.xib;
276 | sourceTree = "";
277 | };
278 | /* End PBXVariantGroup section */
279 |
280 | /* Begin XCBuildConfiguration section */
281 | 5CC505021B0F01D3004D0CB1 /* Debug */ = {
282 | isa = XCBuildConfiguration;
283 | buildSettings = {
284 | ALWAYS_SEARCH_USER_PATHS = NO;
285 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
287 | CLANG_CXX_LIBRARY = "libc++";
288 | CLANG_ENABLE_MODULES = YES;
289 | CLANG_ENABLE_OBJC_ARC = YES;
290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
291 | CLANG_WARN_BOOL_CONVERSION = YES;
292 | CLANG_WARN_COMMA = YES;
293 | CLANG_WARN_CONSTANT_CONVERSION = YES;
294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
296 | CLANG_WARN_EMPTY_BODY = YES;
297 | CLANG_WARN_ENUM_CONVERSION = YES;
298 | CLANG_WARN_INFINITE_RECURSION = YES;
299 | CLANG_WARN_INT_CONVERSION = YES;
300 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
301 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
302 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
303 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
304 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
305 | CLANG_WARN_STRICT_PROTOTYPES = YES;
306 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
307 | CLANG_WARN_UNREACHABLE_CODE = YES;
308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
310 | COPY_PHASE_STRIP = NO;
311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
312 | ENABLE_STRICT_OBJC_MSGSEND = YES;
313 | ENABLE_TESTABILITY = YES;
314 | GCC_C_LANGUAGE_STANDARD = gnu99;
315 | GCC_DYNAMIC_NO_PIC = NO;
316 | GCC_NO_COMMON_BLOCKS = YES;
317 | GCC_OPTIMIZATION_LEVEL = 0;
318 | GCC_PREPROCESSOR_DEFINITIONS = (
319 | "DEBUG=1",
320 | "$(inherited)",
321 | );
322 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
323 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
324 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
325 | GCC_WARN_UNDECLARED_SELECTOR = YES;
326 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
327 | GCC_WARN_UNUSED_FUNCTION = YES;
328 | GCC_WARN_UNUSED_VARIABLE = YES;
329 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
330 | MTL_ENABLE_DEBUG_INFO = YES;
331 | ONLY_ACTIVE_ARCH = YES;
332 | SDKROOT = iphoneos;
333 | };
334 | name = Debug;
335 | };
336 | 5CC505031B0F01D3004D0CB1 /* Release */ = {
337 | isa = XCBuildConfiguration;
338 | buildSettings = {
339 | ALWAYS_SEARCH_USER_PATHS = NO;
340 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
341 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
342 | CLANG_CXX_LIBRARY = "libc++";
343 | CLANG_ENABLE_MODULES = YES;
344 | CLANG_ENABLE_OBJC_ARC = YES;
345 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
346 | CLANG_WARN_BOOL_CONVERSION = YES;
347 | CLANG_WARN_COMMA = YES;
348 | CLANG_WARN_CONSTANT_CONVERSION = YES;
349 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
350 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
351 | CLANG_WARN_EMPTY_BODY = YES;
352 | CLANG_WARN_ENUM_CONVERSION = YES;
353 | CLANG_WARN_INFINITE_RECURSION = YES;
354 | CLANG_WARN_INT_CONVERSION = YES;
355 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
356 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
357 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
359 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
360 | CLANG_WARN_STRICT_PROTOTYPES = YES;
361 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
362 | CLANG_WARN_UNREACHABLE_CODE = YES;
363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
364 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
365 | COPY_PHASE_STRIP = NO;
366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
367 | ENABLE_NS_ASSERTIONS = NO;
368 | ENABLE_STRICT_OBJC_MSGSEND = YES;
369 | GCC_C_LANGUAGE_STANDARD = gnu99;
370 | GCC_NO_COMMON_BLOCKS = YES;
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
378 | MTL_ENABLE_DEBUG_INFO = NO;
379 | SDKROOT = iphoneos;
380 | VALIDATE_PRODUCT = YES;
381 | };
382 | name = Release;
383 | };
384 | 5CC505051B0F01D3004D0CB1 /* Debug */ = {
385 | isa = XCBuildConfiguration;
386 | baseConfigurationReference = C28BF2C6C8C5354952538019 /* Pods-LeanCloudFeedbackDemo.debug.xcconfig */;
387 | buildSettings = {
388 | ALWAYS_SEARCH_USER_PATHS = NO;
389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
390 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
391 | DEVELOPMENT_TEAM = 7J5XFNL99Q;
392 | ENABLE_BITCODE = NO;
393 | FRAMEWORK_SEARCH_PATHS = (
394 | "$(inherited)",
395 | "$(PROJECT_DIR)",
396 | ..,
397 | );
398 | HEADER_SEARCH_PATHS = (
399 | "\"$(OBJROOT)/UninstalledProducts/include\"",
400 | "$(inherited)",
401 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
402 | );
403 | INFOPLIST_FILE = LeanCloudFeedbackDemo/Info.plist;
404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
405 | PRODUCT_BUNDLE_IDENTIFIER = "com.leancloud.$(PRODUCT_NAME:rfc1034identifier)";
406 | PRODUCT_NAME = "$(TARGET_NAME)";
407 | };
408 | name = Debug;
409 | };
410 | 5CC505061B0F01D3004D0CB1 /* Release */ = {
411 | isa = XCBuildConfiguration;
412 | baseConfigurationReference = 7465E688A3D8D6072420F996 /* Pods-LeanCloudFeedbackDemo.release.xcconfig */;
413 | buildSettings = {
414 | ALWAYS_SEARCH_USER_PATHS = NO;
415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
416 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
417 | DEVELOPMENT_TEAM = 7J5XFNL99Q;
418 | ENABLE_BITCODE = NO;
419 | FRAMEWORK_SEARCH_PATHS = (
420 | "$(inherited)",
421 | "$(PROJECT_DIR)",
422 | ..,
423 | );
424 | HEADER_SEARCH_PATHS = (
425 | "\"$(OBJROOT)/UninstalledProducts/include\"",
426 | "$(inherited)",
427 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
428 | );
429 | INFOPLIST_FILE = LeanCloudFeedbackDemo/Info.plist;
430 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
431 | PRODUCT_BUNDLE_IDENTIFIER = "com.leancloud.$(PRODUCT_NAME:rfc1034identifier)";
432 | PRODUCT_NAME = "$(TARGET_NAME)";
433 | };
434 | name = Release;
435 | };
436 | /* End XCBuildConfiguration section */
437 |
438 | /* Begin XCConfigurationList section */
439 | 5CC504DC1B0F01D3004D0CB1 /* Build configuration list for PBXProject "LeanCloudFeedbackDemo" */ = {
440 | isa = XCConfigurationList;
441 | buildConfigurations = (
442 | 5CC505021B0F01D3004D0CB1 /* Debug */,
443 | 5CC505031B0F01D3004D0CB1 /* Release */,
444 | );
445 | defaultConfigurationIsVisible = 0;
446 | defaultConfigurationName = Release;
447 | };
448 | 5CC505041B0F01D3004D0CB1 /* Build configuration list for PBXNativeTarget "LeanCloudFeedbackDemo" */ = {
449 | isa = XCConfigurationList;
450 | buildConfigurations = (
451 | 5CC505051B0F01D3004D0CB1 /* Debug */,
452 | 5CC505061B0F01D3004D0CB1 /* Release */,
453 | );
454 | defaultConfigurationIsVisible = 0;
455 | defaultConfigurationName = Release;
456 | };
457 | /* End XCConfigurationList section */
458 | };
459 | rootObject = 5CC504D91B0F01D3004D0CB1 /* Project object */;
460 | }
461 |
--------------------------------------------------------------------------------
/LeanCloudFeedback/LCUserFeedbackViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCUserFeedbackViewController.m
3 | // Feedback
4 | //
5 | // Created by yang chaozhong on 4/24/14.
6 | // Copyright (c) 2014 LeanCloud. All rights reserved.
7 | //
8 |
9 | #import "LCUserFeedbackViewController.h"
10 | #import "LCUserFeedbackReplyCell.h"
11 | #import "LCUserFeedbackThread.h"
12 | #import "LCUserFeedbackThread_Internal.h"
13 | #import "LCUserFeedbackReply_Internal.h"
14 | #import "LCUserFeedbackReply.h"
15 | #import "LCUserFeedbackAgent.h"
16 | #import "LCUserFeedbackImageViewController.h"
17 | #import "LCUtils.h"
18 |
19 | #define kInputViewColor [UIColor colorWithRed:247.0f/255 green:248.0f/255 blue:248.0f/255 alpha:1]
20 |
21 | #define TAG_TABLEView_Header 1
22 | #define TAG_InputFiled 2
23 |
24 | static CGFloat const kInputViewHeight = 48;
25 | static CGFloat const kContactHeaderHeight = 48;
26 | static CGFloat const kAddImageButtonWidth = 40;
27 | static CGFloat const kSendButtonWidth = 60;
28 |
29 | @interface LCUserFeedbackViewController () {
30 | NSMutableArray *_feedbackReplies;
31 | LCUserFeedbackThread *_userFeedback;
32 | }
33 |
34 | @property(nonatomic, strong) UIRefreshControl *refreshControl;
35 | @property(nonatomic, strong) UITableView *tableView;
36 | @property(nonatomic, strong) UITextField *tableViewHeader;
37 | @property(nonatomic, strong) UITextField *inputTextField;
38 | @property(nonatomic, strong) UIButton *addImageButton;
39 | @property(nonatomic, strong) UIButton *sendButton;
40 | @property(nonatomic, strong) UIBarButtonItem *closeButtonItem;
41 |
42 | @end
43 |
44 | @implementation LCUserFeedbackViewController
45 |
46 | - (instancetype)init
47 | {
48 | self = [super init];
49 | if (self) {
50 | _feedbackReplies = [[NSMutableArray alloc] init];
51 | // Custom initialization
52 | _navigationBarStyle = LCUserFeedbackNavigationBarStyleBlue;
53 | _contactHeaderHidden = NO;
54 | _feedbackCellFont = [UIFont systemFontOfSize:16];
55 | _presented = YES;
56 | }
57 | return self;
58 | }
59 |
60 | - (void)viewDidLoad
61 | {
62 | [super viewDidLoad];
63 | [self setupUI];
64 | // [self keyboardWillHide:nil];
65 | [self loadFeedbackThreads];
66 | }
67 |
68 | - (void)viewDidAppear:(BOOL)animated {
69 | [super viewDidAppear:animated];
70 | }
71 |
72 | - (void)viewDidDisappear:(BOOL)animated {
73 | [super viewDidDisappear:animated];
74 | // 记录最后阅读时,看到了多少条反馈
75 | if (_userFeedback !=nil && _feedbackReplies.count > 0) {
76 | NSString *localKey = [NSString stringWithFormat:@"feedback_%@", _userFeedback.objectId];
77 | [[NSUserDefaults standardUserDefaults] setObject:@(_feedbackReplies.count) forKey:localKey];
78 | }
79 | }
80 |
81 | - (void)setupUI {
82 | if (self.presented) {
83 | self.navigationItem.leftBarButtonItem = self.closeButtonItem;
84 | }
85 |
86 | [self.navigationItem setTitle:LCLocalizedString(@"User Feedback")];
87 | [self setupNavigaionBar];
88 |
89 | [self.view addSubview:self.tableView];
90 | [self.view addSubview:self.addImageButton];
91 | [self.view addSubview:self.sendButton];
92 | [self.view addSubview:self.inputTextField];
93 |
94 | [[[LCUserFeedbackReplyCell class] appearance] setCellFont:self.feedbackCellFont];
95 |
96 | [self.tableView addSubview:self.refreshControl];
97 |
98 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyboard:)];
99 | tap.cancelsTouchesInView = NO;
100 | [self.tableView addGestureRecognizer:tap];
101 | }
102 |
103 | #pragma mark - Properties
104 |
105 | - (UIButton *)closeButton {
106 | UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
107 | UIImage *closeButtonImage = [UIImage imageNamed:@"feedback_back"];
108 | [closeButton setImage:closeButtonImage forState:UIControlStateNormal];
109 | closeButton.frame = CGRectMake(0, 0, closeButtonImage.size.width, closeButtonImage.size.height);
110 | closeButton.autoresizingMask = UIViewAutoresizingFlexibleHeight;
111 | return closeButton;
112 | }
113 |
114 | - (UIBarButtonItem *)closeButtonItem {
115 | if (_closeButtonItem == nil) {
116 | if (self.presented) {
117 | _closeButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemStop target:self action:@selector(closeButtonClicked:)];
118 | } else {
119 | UIButton *closeButton = [self closeButton];
120 | [closeButton addTarget:self action:@selector(closeButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
121 | _closeButtonItem = [[UIBarButtonItem alloc] initWithCustomView:closeButton];
122 | }
123 |
124 | }
125 | return _closeButtonItem;
126 | }
127 |
128 |
129 | - (UITableView *)tableView {
130 | if (_tableView == nil) {
131 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - kInputViewHeight)
132 | style:UITableViewStylePlain];
133 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
134 | _tableView.delegate = self;
135 | _tableView.dataSource = self;
136 | }
137 | return _tableView;
138 | }
139 |
140 | - (UIButton *)addImageButton {
141 | if (_addImageButton == nil) {
142 | _addImageButton = [[UIButton alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.view.frame) - kInputViewHeight, kAddImageButtonWidth, kInputViewHeight)];
143 | _addImageButton.backgroundColor = kInputViewColor;
144 | [_addImageButton setImage:[UIImage imageNamed:@"feedback_add_image"] forState:UIControlStateNormal];
145 | _addImageButton.contentMode = UIViewContentModeScaleAspectFill;
146 | [_addImageButton addTarget:self action:@selector(addImageButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
147 | }
148 | return _addImageButton;
149 | }
150 |
151 | - (UIButton *)sendButton {
152 | if (_sendButton == nil) {
153 | _sendButton = [UIButton buttonWithType:UIButtonTypeSystem];
154 | _sendButton.frame = CGRectMake(CGRectGetWidth(self.view.frame) - kSendButtonWidth, CGRectGetHeight(self.view.frame) - kInputViewHeight, kSendButtonWidth, kInputViewHeight);
155 | [_sendButton.titleLabel setFont:[UIFont systemFontOfSize:12]];
156 | [_sendButton setTitleColor:[UIColor colorWithRed:137.0f/255 green:137.0f/255 blue:137.0f/255 alpha:1] forState:UIControlStateNormal];
157 | [_sendButton setTitle:LCLocalizedString(@"Send") forState:UIControlStateNormal];
158 | [_sendButton setBackgroundColor: kInputViewColor];
159 | [_sendButton addTarget:self action:@selector(sendButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
160 | }
161 | return _sendButton;
162 | }
163 |
164 | - (UITextField *)inputTextField {
165 | if (_inputTextField == nil) {
166 | _inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(kAddImageButtonWidth, CGRectGetMinY(self.sendButton.frame), CGRectGetWidth(self.view.frame)- kSendButtonWidth - kAddImageButtonWidth, kInputViewHeight)];
167 | _inputTextField.tag = TAG_InputFiled;
168 | [_inputTextField setFont:[UIFont systemFontOfSize:12]];
169 | _inputTextField.backgroundColor = kInputViewColor;
170 | _inputTextField.placeholder = LCLocalizedString(@"Please write your feedback");
171 | UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 30)];
172 | _inputTextField.leftView = paddingView;
173 | _inputTextField.leftViewMode = UITextFieldViewModeAlways;
174 | _inputTextField.returnKeyType = UIReturnKeyDone;
175 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
176 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
177 | _inputTextField.delegate = self;
178 | }
179 | return _inputTextField;
180 | }
181 |
182 | - (void)setupNavigaionBar {
183 | switch (self.navigationBarStyle) {
184 | case LCUserFeedbackNavigationBarStyleBlue: {
185 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];
186 | UIColor *blue =[UIColor colorWithRed:85.0f/255 green:184.0f/255 blue:244.0f/255 alpha:1];
187 | self.navigationController.navigationBar.barTintColor = blue;
188 | self.closeButtonItem.tintColor = [UIColor whiteColor];
189 | break;
190 | }
191 | case LCUserFeedbackNavigationBarStyleNone:
192 | break;
193 | default:
194 | break;
195 | }
196 | }
197 |
198 | - (UIRefreshControl *)refreshControl {
199 | if (_refreshControl == nil) {
200 | _refreshControl = [[UIRefreshControl alloc] init];
201 | [_refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
202 | }
203 | return _refreshControl;
204 | }
205 |
206 | #pragma mark - load data
207 |
208 | - (void)fetchRepliesWithBlock:(AVArrayResultBlock)block {
209 | [LCUserFeedbackThread fetchFeedbackWithBlock:^(LCUserFeedbackThread *feedback, NSError *error) {
210 | if (error) {
211 | block(nil, error);
212 | } else {
213 | if (feedback) {
214 | self->_userFeedback = feedback;
215 | if (self.contact == nil) {
216 | self.contact = feedback.contact;
217 | }
218 | [self->_userFeedback fetchFeedbackRepliesInBackgroundWithBlock:block];
219 | } else {
220 | block([NSArray array], nil);
221 | }
222 | }
223 | }];
224 | }
225 |
226 | - (void)loadFeedbackThreads {
227 | if (![_refreshControl isRefreshing]) {
228 | [_refreshControl beginRefreshing];
229 | }
230 | [self fetchRepliesWithBlock:^(NSArray *objects, NSError *error) {
231 | [self->_refreshControl endRefreshing];
232 | if ([self filterError:error]) {
233 | if (objects.count > 0) {
234 | [self->_feedbackReplies removeAllObjects];
235 | [self->_feedbackReplies addObjectsFromArray:objects];
236 |
237 | [self->_tableView reloadData];
238 | [self scrollToBottom];
239 | }
240 | }
241 | }];
242 | }
243 |
244 | - (void)handleRefresh:(id)sender {
245 | [self loadFeedbackThreads];
246 | }
247 |
248 | #pragma mark - util
249 |
250 | - (void)alertWithTitle:(NSString *)title message:(NSString *)message {
251 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
252 | [alertView show];
253 | }
254 |
255 | - (BOOL)filterError:(NSError *)error {
256 | if (error) {
257 | [self alertWithTitle:@"出错了" message:[error description]];
258 | return NO;
259 | } else {
260 | return YES;
261 | }
262 | }
263 |
264 | #pragma mark - send action
265 |
266 | - (void)popImageViewController:(id)sender {
267 | [self.navigationController popViewControllerAnimated:YES];
268 | }
269 |
270 | - (void)didSelectImageViewOnFeedbackReply:(LCUserFeedbackReply *)feedbackReply {
271 | LCUserFeedbackImageViewController *imageViewController = [[LCUserFeedbackImageViewController alloc] init];
272 | imageViewController.image = feedbackReply.attachmentImage;
273 | if (self.navigationBarStyle == LCUserFeedbackNavigationBarStyleBlue) {
274 | UIButton *button = [self closeButton];
275 | [button addTarget:self action:@selector(popImageViewController:) forControlEvents:UIControlEventTouchUpInside];
276 | UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:button];
277 | imageViewController.navigationItem.leftBarButtonItem = item;
278 | }
279 | [self.navigationController pushViewController:imageViewController animated:YES];
280 | }
281 |
282 | - (void)closeButtonClicked:(id)sender {
283 | if (self.presented) {
284 | [self dismissViewControllerAnimated:YES completion:^{
285 |
286 | }];
287 | } else {
288 | [self.navigationController popViewControllerAnimated:YES];
289 | }
290 | }
291 |
292 | - (NSString *)currentContact {
293 | NSString *contact = self.tableViewHeader.text;
294 | return contact.length > 0 ? contact : _contact;
295 | }
296 |
297 | - (void)addImageButtonClicked:(id)sender {
298 | UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
299 | pickerController.navigationBar.barStyle = UIBarStyleBlack;
300 | pickerController.delegate = self;
301 | pickerController.editing = NO;
302 | pickerController.allowsEditing = NO;
303 | pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
304 | [self presentViewController:pickerController animated:YES completion:nil];
305 | }
306 |
307 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
308 | UIImage *originImage = info[UIImagePickerControllerOriginalImage];
309 | [self prepareFeedbackWithBlock:^(BOOL succeeded, NSError *error) {
310 | if ([self filterError:error]) {
311 | AVFile *attachment = [AVFile fileWithData:UIImageJPEGRepresentation(originImage, 0.6) name:@"feedback.png"];
312 | [attachment uploadWithCompletionHandler:^(BOOL succeeded, NSError *error) {
313 | if ([self filterError:error]) {
314 | LCUserFeedbackReply *feedbackReply = [LCUserFeedbackReply feedbackReplyWithAttachment:attachment.url type:LCReplyTypeUser];
315 | feedbackReply.attachmentImage = originImage;
316 | [self saveFeedbackReply:feedbackReply AtFeedback:self->_userFeedback];
317 | }
318 | }];
319 | }
320 | }];
321 | [picker dismissViewControllerAnimated:YES completion:nil];
322 | }
323 |
324 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
325 | [picker dismissViewControllerAnimated:YES completion:nil];
326 | }
327 |
328 | - (void)prepareFeedbackWithBlock:(AVBooleanResultBlock)block {
329 | NSString *contact = [self currentContact];
330 | NSString *content = self.inputTextField.text;
331 | if (_userFeedback) {
332 | block(YES, nil);
333 | } else {
334 | _contact = contact;
335 | NSString *title = _feedbackTitle ?: content;
336 | if (title.length == 0) {
337 | title = @"用户反馈";
338 | }
339 | [LCUserFeedbackThread feedbackWithContent:title contact:_contact create:YES withBlock:^(id object, NSError *error) {
340 | if (error) {
341 | block(NO, error);
342 | } else {
343 | self->_userFeedback = object;
344 | block(YES, nil);
345 | }
346 | }];
347 | }
348 | }
349 |
350 | - (void)sendButtonClicked:(id)sender {
351 | NSString *content = self.inputTextField.text;
352 | if (content.length) {
353 | self.sendButton.enabled = NO;
354 | [self prepareFeedbackWithBlock:^(BOOL succeeded, NSError *error) {
355 | if ([self filterError:error]) {
356 | LCUserFeedbackReply *feedbackReply = [LCUserFeedbackReply feedbackReplyWithContent:content type:LCReplyTypeUser];
357 | [self saveFeedbackReply:feedbackReply AtFeedback:self->_userFeedback];
358 | } else {
359 | self.sendButton.enabled = YES;
360 | }
361 | }];
362 | }
363 | }
364 |
365 | - (void)saveFeedbackReply:(LCUserFeedbackReply *)feedbackReply AtFeedback:(LCUserFeedbackThread *)feedback {
366 | [_userFeedback saveFeedbackReplyInBackground:feedbackReply withBlock:^(id object, NSError *error) {
367 | if ([self filterError:error]) {
368 | [self->_feedbackReplies addObject:feedbackReply];
369 | [self.tableView reloadData];
370 | [self scrollToBottom];
371 |
372 | if ([self->_inputTextField.text length] > 0) {
373 | self->_inputTextField.text = @"";
374 | }
375 | }
376 | self.sendButton.enabled = YES;
377 | }];
378 | }
379 |
380 | - (void)didReceiveMemoryWarning
381 | {
382 | [super didReceiveMemoryWarning];
383 | }
384 |
385 | #pragma mark - UIKeyboard Notification
386 |
387 | - (void)updateHeightWhenKeyboardHide:(UIView *)bottomView {
388 | CGRect bottomViewFrame = bottomView.frame;
389 | bottomViewFrame.origin.y = self.view.bounds.size.height - bottomViewFrame.size.height;
390 | bottomView.frame = bottomViewFrame;
391 | }
392 |
393 | - (void)updateHeightWhenKeyboardShow:(UIView *)bottomView keyboardHeight:(CGFloat)keyboardHeight{
394 | CGRect bottomViewFrame = bottomView.frame;
395 | bottomViewFrame.origin.y = self.view.bounds.size.height - keyboardHeight - bottomViewFrame.size.height;
396 | bottomView.frame = bottomViewFrame;
397 | }
398 |
399 | - (void)keyboardWillShow:(NSNotification *)notification {
400 | if ([self.tableViewHeader isFirstResponder]) {
401 | return;
402 | }
403 |
404 | float animationDuration = [[[notification userInfo] valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
405 | CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
406 |
407 | [UIView animateWithDuration:animationDuration
408 | delay:0
409 | options:UIViewAnimationOptionCurveEaseInOut
410 | animations:^{
411 |
412 | [self updateHeightWhenKeyboardShow:self.addImageButton keyboardHeight:keyboardHeight];
413 | [self updateHeightWhenKeyboardShow:self.inputTextField keyboardHeight:keyboardHeight];
414 | [self updateHeightWhenKeyboardShow:self.sendButton keyboardHeight:keyboardHeight];
415 |
416 | CGRect tableViewFrame = self.tableView.frame;
417 | tableViewFrame.size.height = self.view.bounds.size.height - self.navigationController.navigationBar.frame.size.height - keyboardHeight;
418 | self.tableView.frame = tableViewFrame;
419 | }
420 | completion:^(BOOL finished) {
421 | [self scrollToBottom];
422 | }
423 | ];
424 | }
425 |
426 | - (void)keyboardWillHide:(NSNotification *)notification {
427 | if ([self.tableViewHeader isFirstResponder]) {
428 | return;
429 | }
430 |
431 | [UIView beginAnimations:@"bottomBarDown" context:nil];
432 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
433 |
434 | [self updateHeightWhenKeyboardHide:self.addImageButton];
435 | [self updateHeightWhenKeyboardHide:self.inputTextField];
436 | [self updateHeightWhenKeyboardHide:self.sendButton];
437 |
438 | CGRect tableViewFrame = self.tableView.frame;
439 | tableViewFrame.size.height = CGRectGetHeight(self.view.frame) - kInputViewHeight;
440 | self.tableView.frame = tableViewFrame;
441 |
442 | [UIView commitAnimations];
443 | }
444 |
445 | - (void)closeKeyboard:(id)sender {
446 | [self.inputTextField resignFirstResponder];
447 | }
448 |
449 | #pragma mark UITextField Delegate Methods
450 |
451 | - (BOOL)textFieldShouldReturn:(UITextField *)textField {
452 | [textField resignFirstResponder];
453 |
454 | return YES;
455 | }
456 |
457 | - (void)textFieldDidEndEditing:(UITextField *)textField {
458 | if (textField.tag == TAG_TABLEView_Header && [textField.text length] > 0 && _userFeedback) {
459 | _userFeedback.contact = textField.text;
460 | [LCUserFeedbackThread updateFeedback:_userFeedback withBlock:^(id object, NSError *error) {
461 | if ([self filterError:error]) {
462 | [self alertWithTitle:@"提示" message:@"更改成功"];
463 | }
464 | }];
465 | }
466 | }
467 |
468 | #pragma mark - UIScrollViewDelegate
469 | - (void)scrollToBottom {
470 | if ([self.tableView numberOfRowsInSection:0] > 1) {
471 | NSInteger lastRowNumber = [self.tableView numberOfRowsInSection:0] - 1;
472 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
473 | [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
474 | }
475 | }
476 |
477 | #pragma mark - UITableViewDelegate and UITableViewDataSource
478 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
479 | return 1;
480 | }
481 |
482 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
483 | return [_feedbackReplies count];
484 | }
485 |
486 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
487 | if (self.contactHeaderHidden) {
488 | return 0;
489 | } else {
490 | return kContactHeaderHeight;
491 | }
492 | }
493 |
494 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
495 | self.tableViewHeader = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, kContactHeaderHeight)];
496 | UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 30)];
497 | self.tableViewHeader.leftView = paddingView;
498 | self.tableViewHeader.leftViewMode = UITextFieldViewModeAlways;
499 |
500 | self.tableViewHeader.delegate = (id ) self;
501 | self.tableViewHeader.tag = TAG_TABLEView_Header;
502 | [self.tableViewHeader setBackgroundColor:[UIColor colorWithRed:247.0f/255 green:248.0f/255 blue:248.0f/255 alpha:1]];
503 | self.tableViewHeader.textAlignment = NSTextAlignmentLeft;
504 | self.tableViewHeader.placeholder = LCLocalizedString(@"Email Or QQ");
505 | [self.tableViewHeader setFont:[UIFont systemFontOfSize:12.0f]];
506 | if (_contact) {
507 | self.tableViewHeader.text = _contact;
508 | }
509 | return _tableViewHeader;
510 | }
511 |
512 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
513 | return [LCUserFeedbackReplyCell heightForFeedbackReply:(LCUserFeedbackReply *)_feedbackReplies[indexPath.row]];
514 | }
515 |
516 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
517 | LCUserFeedbackReply *feedbackReply = _feedbackReplies[indexPath.row];
518 | static NSString *cellIdentifier = @"feedbackReplyCell";
519 | LCUserFeedbackReplyCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
520 | if (cell == nil) {
521 | cell = [[LCUserFeedbackReplyCell alloc] initWithFeedbackReply:feedbackReply reuseIdentifier:cellIdentifier];;
522 | cell.delegate = self;
523 | }
524 | [cell configuareCellWithFeedbackReply:feedbackReply];
525 | return cell;
526 | }
527 |
528 | @end
529 |
530 |
531 |
--------------------------------------------------------------------------------
/LeanCloudFeedback.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | 5CC504C71B0EFCBA004D0CB1 /* UniversalFramework */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = 5CC504CA1B0EFCBA004D0CB1 /* Build configuration list for PBXAggregateTarget "UniversalFramework" */;
13 | buildPhases = (
14 | 5CC504CB1B0EFCC0004D0CB1 /* ShellScript */,
15 | );
16 | dependencies = (
17 | );
18 | name = UniversalFramework;
19 | productName = UniversalFramework;
20 | };
21 | /* End PBXAggregateTarget section */
22 |
23 | /* Begin PBXBuildFile section */
24 | 5CCAE1011B09B0FE009276B9 /* LeanCloudFeedback.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE1001B09B0FE009276B9 /* LeanCloudFeedback.h */; settings = {ATTRIBUTES = (Public, ); }; };
25 | 5CCAE1241B09B323009276B9 /* LCUserFeedbackAgent.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1171B09B323009276B9 /* LCUserFeedbackAgent.m */; };
26 | 5CCAE1251B09B323009276B9 /* LCUserFeedbackThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1181B09B323009276B9 /* LCUserFeedbackThread.m */; };
27 | 5CCAE1261B09B323009276B9 /* LCUserFeedbackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1191B09B323009276B9 /* LCUserFeedbackViewController.m */; };
28 | 5CCAE1271B09B323009276B9 /* LCUserFeedbackThread_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE11A1B09B323009276B9 /* LCUserFeedbackThread_Internal.h */; };
29 | 5CCAE1281B09B323009276B9 /* LCUserFeedbackThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE11B1B09B323009276B9 /* LCUserFeedbackThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
30 | 5CCAE1291B09B323009276B9 /* LCUserFeedbackAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE11C1B09B323009276B9 /* LCUserFeedbackAgent.h */; settings = {ATTRIBUTES = (Public, ); }; };
31 | 5CCAE12A1B09B323009276B9 /* LCUserFeedbackReplyCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE11D1B09B323009276B9 /* LCUserFeedbackReplyCell.h */; };
32 | 5CCAE12B1B09B323009276B9 /* LCUserFeedbackReplyCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE11E1B09B323009276B9 /* LCUserFeedbackReplyCell.m */; };
33 | 5CCAE12E1B09B323009276B9 /* LCUserFeedbackReply.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE1211B09B323009276B9 /* LCUserFeedbackReply.h */; settings = {ATTRIBUTES = (Public, ); }; };
34 | 5CCAE12F1B09B323009276B9 /* LCUserFeedbackReply.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1221B09B323009276B9 /* LCUserFeedbackReply.m */; };
35 | 5CCAE1301B09B323009276B9 /* LCUserFeedbackViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE1231B09B323009276B9 /* LCUserFeedbackViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
36 | 5CCAE1351B09C1F2009276B9 /* LCHttpClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1311B09C1F2009276B9 /* LCHttpClient.m */; };
37 | 5CCAE1361B09C1F2009276B9 /* LCHttpClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE1321B09C1F2009276B9 /* LCHttpClient.h */; };
38 | 5CCAE1371B09C1F2009276B9 /* LCUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CCAE1331B09C1F2009276B9 /* LCUtils.m */; };
39 | 5CCAE1381B09C1F2009276B9 /* LCUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CCAE1341B09C1F2009276B9 /* LCUtils.h */; };
40 | 7000FCF21BEC9E450097C729 /* LeanCloudFeedback.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7000FCF11BEC9E450097C729 /* LeanCloudFeedback.strings */; };
41 | 703074C31B9ABA6D00FD796E /* feedback_back@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074BC1B9ABA6D00FD796E /* feedback_back@2x.png */; };
42 | 703074C41B9ABA6D00FD796E /* feedback_bg_1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074BD1B9ABA6D00FD796E /* feedback_bg_1@2x.png */; };
43 | 703074C51B9ABA6D00FD796E /* feedback_bg_2@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074BE1B9ABA6D00FD796E /* feedback_bg_2@2x.png */; };
44 | 703074C61B9ABA6D00FD796E /* feedback_btn@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074BF1B9ABA6D00FD796E /* feedback_btn@2x.png */; };
45 | 703074C71B9ABA6D00FD796E /* feedback_btn_new@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074C01B9ABA6D00FD796E /* feedback_btn_new@2x.png */; };
46 | 703074C81B9ABA6D00FD796E /* feedback_warning@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074C11B9ABA6D00FD796E /* feedback_warning@2x.png */; };
47 | 703074E81B9ABDA400FD796E /* feedback_add_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 703074E71B9ABDA400FD796E /* feedback_add_image@2x.png */; };
48 | 70FB0B271B5E76D500FD8D70 /* LCUserFeedbackImageViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 70FB0B251B5E76D500FD8D70 /* LCUserFeedbackImageViewController.h */; };
49 | 70FB0B281B5E76D500FD8D70 /* LCUserFeedbackImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 70FB0B261B5E76D500FD8D70 /* LCUserFeedbackImageViewController.m */; };
50 | FA5979E6FAE10100718387EA /* libPods-LeanCloudFeedback.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 62FC55FE5788FF58EC0BC812 /* libPods-LeanCloudFeedback.a */; };
51 | /* End PBXBuildFile section */
52 |
53 | /* Begin PBXFileReference section */
54 | 59AC16570E133BA472B3DABB /* Pods-LeanCloudFeedback.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeanCloudFeedback.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeanCloudFeedback/Pods-LeanCloudFeedback.release.xcconfig"; sourceTree = ""; };
55 | 5CCAE0FB1B09B0FE009276B9 /* LeanCloudFeedback.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LeanCloudFeedback.framework; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 5CCAE0FF1B09B0FE009276B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
57 | 5CCAE1001B09B0FE009276B9 /* LeanCloudFeedback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LeanCloudFeedback.h; sourceTree = ""; };
58 | 5CCAE1171B09B323009276B9 /* LCUserFeedbackAgent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackAgent.m; sourceTree = ""; };
59 | 5CCAE1181B09B323009276B9 /* LCUserFeedbackThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackThread.m; sourceTree = ""; };
60 | 5CCAE1191B09B323009276B9 /* LCUserFeedbackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackViewController.m; sourceTree = ""; };
61 | 5CCAE11A1B09B323009276B9 /* LCUserFeedbackThread_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackThread_Internal.h; sourceTree = ""; };
62 | 5CCAE11B1B09B323009276B9 /* LCUserFeedbackThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackThread.h; sourceTree = ""; };
63 | 5CCAE11C1B09B323009276B9 /* LCUserFeedbackAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackAgent.h; sourceTree = ""; };
64 | 5CCAE11D1B09B323009276B9 /* LCUserFeedbackReplyCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackReplyCell.h; sourceTree = ""; };
65 | 5CCAE11E1B09B323009276B9 /* LCUserFeedbackReplyCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackReplyCell.m; sourceTree = ""; };
66 | 5CCAE1211B09B323009276B9 /* LCUserFeedbackReply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackReply.h; sourceTree = ""; };
67 | 5CCAE1221B09B323009276B9 /* LCUserFeedbackReply.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackReply.m; sourceTree = ""; };
68 | 5CCAE1231B09B323009276B9 /* LCUserFeedbackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackViewController.h; sourceTree = ""; };
69 | 5CCAE1311B09C1F2009276B9 /* LCHttpClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCHttpClient.m; sourceTree = ""; };
70 | 5CCAE1321B09C1F2009276B9 /* LCHttpClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCHttpClient.h; sourceTree = ""; };
71 | 5CCAE1331B09C1F2009276B9 /* LCUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUtils.m; sourceTree = ""; };
72 | 5CCAE1341B09C1F2009276B9 /* LCUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUtils.h; sourceTree = ""; };
73 | 62FC55FE5788FF58EC0BC812 /* libPods-LeanCloudFeedback.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LeanCloudFeedback.a"; sourceTree = BUILT_PRODUCTS_DIR; };
74 | 7000FCF11BEC9E450097C729 /* LeanCloudFeedback.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = LeanCloudFeedback.strings; sourceTree = ""; };
75 | 703074BC1B9ABA6D00FD796E /* feedback_back@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_back@2x.png"; sourceTree = ""; };
76 | 703074BD1B9ABA6D00FD796E /* feedback_bg_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_bg_1@2x.png"; sourceTree = ""; };
77 | 703074BE1B9ABA6D00FD796E /* feedback_bg_2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_bg_2@2x.png"; sourceTree = ""; };
78 | 703074BF1B9ABA6D00FD796E /* feedback_btn@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_btn@2x.png"; sourceTree = ""; };
79 | 703074C01B9ABA6D00FD796E /* feedback_btn_new@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_btn_new@2x.png"; sourceTree = ""; };
80 | 703074C11B9ABA6D00FD796E /* feedback_warning@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_warning@2x.png"; sourceTree = ""; };
81 | 703074E71B9ABDA400FD796E /* feedback_add_image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "feedback_add_image@2x.png"; sourceTree = ""; };
82 | 70FB0B1E1B5E0E4F00FD8D70 /* LCUserFeedbackReply_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackReply_Internal.h; sourceTree = ""; };
83 | 70FB0B251B5E76D500FD8D70 /* LCUserFeedbackImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCUserFeedbackImageViewController.h; sourceTree = ""; };
84 | 70FB0B261B5E76D500FD8D70 /* LCUserFeedbackImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCUserFeedbackImageViewController.m; sourceTree = ""; };
85 | D94FDB034BF527C80C647B30 /* Pods-LeanCloudFeedback.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeanCloudFeedback.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LeanCloudFeedback/Pods-LeanCloudFeedback.debug.xcconfig"; sourceTree = ""; };
86 | /* End PBXFileReference section */
87 |
88 | /* Begin PBXFrameworksBuildPhase section */
89 | 5CCAE0F71B09B0FE009276B9 /* Frameworks */ = {
90 | isa = PBXFrameworksBuildPhase;
91 | buildActionMask = 2147483647;
92 | files = (
93 | FA5979E6FAE10100718387EA /* libPods-LeanCloudFeedback.a in Frameworks */,
94 | );
95 | runOnlyForDeploymentPostprocessing = 0;
96 | };
97 | /* End PBXFrameworksBuildPhase section */
98 |
99 | /* Begin PBXGroup section */
100 | 56A155F7F6474AB06BF1EFA0 /* Frameworks */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 62FC55FE5788FF58EC0BC812 /* libPods-LeanCloudFeedback.a */,
104 | );
105 | name = Frameworks;
106 | sourceTree = "";
107 | };
108 | 5CCAE0F11B09B0FE009276B9 = {
109 | isa = PBXGroup;
110 | children = (
111 | 5CCAE0FD1B09B0FE009276B9 /* LeanCloudFeedback */,
112 | 5CCAE0FC1B09B0FE009276B9 /* Products */,
113 | 56A155F7F6474AB06BF1EFA0 /* Frameworks */,
114 | BFE6ABFA49B1037F183EDE3F /* Pods */,
115 | );
116 | sourceTree = "";
117 | };
118 | 5CCAE0FC1B09B0FE009276B9 /* Products */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 5CCAE0FB1B09B0FE009276B9 /* LeanCloudFeedback.framework */,
122 | );
123 | name = Products;
124 | sourceTree = "";
125 | };
126 | 5CCAE0FD1B09B0FE009276B9 /* LeanCloudFeedback */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 703074BA1B9ABA6D00FD796E /* resources */,
130 | 5CCAE1321B09C1F2009276B9 /* LCHttpClient.h */,
131 | 5CCAE1311B09C1F2009276B9 /* LCHttpClient.m */,
132 | 5CCAE1341B09C1F2009276B9 /* LCUtils.h */,
133 | 5CCAE1331B09C1F2009276B9 /* LCUtils.m */,
134 | 5CCAE1001B09B0FE009276B9 /* LeanCloudFeedback.h */,
135 | 5CCAE11C1B09B323009276B9 /* LCUserFeedbackAgent.h */,
136 | 5CCAE1171B09B323009276B9 /* LCUserFeedbackAgent.m */,
137 | 5CCAE11B1B09B323009276B9 /* LCUserFeedbackThread.h */,
138 | 5CCAE11A1B09B323009276B9 /* LCUserFeedbackThread_Internal.h */,
139 | 5CCAE1181B09B323009276B9 /* LCUserFeedbackThread.m */,
140 | 5CCAE1211B09B323009276B9 /* LCUserFeedbackReply.h */,
141 | 70FB0B1E1B5E0E4F00FD8D70 /* LCUserFeedbackReply_Internal.h */,
142 | 5CCAE1221B09B323009276B9 /* LCUserFeedbackReply.m */,
143 | 5CCAE11D1B09B323009276B9 /* LCUserFeedbackReplyCell.h */,
144 | 5CCAE11E1B09B323009276B9 /* LCUserFeedbackReplyCell.m */,
145 | 5CCAE1231B09B323009276B9 /* LCUserFeedbackViewController.h */,
146 | 5CCAE1191B09B323009276B9 /* LCUserFeedbackViewController.m */,
147 | 70FB0B251B5E76D500FD8D70 /* LCUserFeedbackImageViewController.h */,
148 | 70FB0B261B5E76D500FD8D70 /* LCUserFeedbackImageViewController.m */,
149 | 5CCAE0FE1B09B0FE009276B9 /* Supporting Files */,
150 | );
151 | path = LeanCloudFeedback;
152 | sourceTree = "";
153 | };
154 | 5CCAE0FE1B09B0FE009276B9 /* Supporting Files */ = {
155 | isa = PBXGroup;
156 | children = (
157 | 5CCAE0FF1B09B0FE009276B9 /* Info.plist */,
158 | );
159 | name = "Supporting Files";
160 | sourceTree = "";
161 | };
162 | 703074BA1B9ABA6D00FD796E /* resources */ = {
163 | isa = PBXGroup;
164 | children = (
165 | 703074E71B9ABDA400FD796E /* feedback_add_image@2x.png */,
166 | 703074BC1B9ABA6D00FD796E /* feedback_back@2x.png */,
167 | 703074BD1B9ABA6D00FD796E /* feedback_bg_1@2x.png */,
168 | 703074BE1B9ABA6D00FD796E /* feedback_bg_2@2x.png */,
169 | 703074BF1B9ABA6D00FD796E /* feedback_btn@2x.png */,
170 | 703074C01B9ABA6D00FD796E /* feedback_btn_new@2x.png */,
171 | 703074C11B9ABA6D00FD796E /* feedback_warning@2x.png */,
172 | 7000FCF11BEC9E450097C729 /* LeanCloudFeedback.strings */,
173 | );
174 | path = resources;
175 | sourceTree = "";
176 | };
177 | BFE6ABFA49B1037F183EDE3F /* Pods */ = {
178 | isa = PBXGroup;
179 | children = (
180 | D94FDB034BF527C80C647B30 /* Pods-LeanCloudFeedback.debug.xcconfig */,
181 | 59AC16570E133BA472B3DABB /* Pods-LeanCloudFeedback.release.xcconfig */,
182 | );
183 | name = Pods;
184 | sourceTree = "";
185 | };
186 | /* End PBXGroup section */
187 |
188 | /* Begin PBXHeadersBuildPhase section */
189 | 5CCAE0F81B09B0FE009276B9 /* Headers */ = {
190 | isa = PBXHeadersBuildPhase;
191 | buildActionMask = 2147483647;
192 | files = (
193 | 5CCAE1291B09B323009276B9 /* LCUserFeedbackAgent.h in Headers */,
194 | 5CCAE1281B09B323009276B9 /* LCUserFeedbackThread.h in Headers */,
195 | 5CCAE12E1B09B323009276B9 /* LCUserFeedbackReply.h in Headers */,
196 | 5CCAE1361B09C1F2009276B9 /* LCHttpClient.h in Headers */,
197 | 5CCAE1301B09B323009276B9 /* LCUserFeedbackViewController.h in Headers */,
198 | 70FB0B271B5E76D500FD8D70 /* LCUserFeedbackImageViewController.h in Headers */,
199 | 5CCAE1381B09C1F2009276B9 /* LCUtils.h in Headers */,
200 | 5CCAE1271B09B323009276B9 /* LCUserFeedbackThread_Internal.h in Headers */,
201 | 5CCAE1011B09B0FE009276B9 /* LeanCloudFeedback.h in Headers */,
202 | 5CCAE12A1B09B323009276B9 /* LCUserFeedbackReplyCell.h in Headers */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXHeadersBuildPhase section */
207 |
208 | /* Begin PBXNativeTarget section */
209 | 5CCAE0FA1B09B0FE009276B9 /* LeanCloudFeedback */ = {
210 | isa = PBXNativeTarget;
211 | buildConfigurationList = 5CCAE1111B09B0FE009276B9 /* Build configuration list for PBXNativeTarget "LeanCloudFeedback" */;
212 | buildPhases = (
213 | 84898830CC4C08815C7F6A2D /* [CP] Check Pods Manifest.lock */,
214 | 5CCAE0F61B09B0FE009276B9 /* Sources */,
215 | 5CCAE0F71B09B0FE009276B9 /* Frameworks */,
216 | 5CCAE0F81B09B0FE009276B9 /* Headers */,
217 | 5CCAE0F91B09B0FE009276B9 /* Resources */,
218 | 96A4361C0F19D5AEB1038786 /* [CP] Copy Pods Resources */,
219 | );
220 | buildRules = (
221 | );
222 | dependencies = (
223 | );
224 | name = LeanCloudFeedback;
225 | productName = LeanCloudFeedback;
226 | productReference = 5CCAE0FB1B09B0FE009276B9 /* LeanCloudFeedback.framework */;
227 | productType = "com.apple.product-type.framework";
228 | };
229 | /* End PBXNativeTarget section */
230 |
231 | /* Begin PBXProject section */
232 | 5CCAE0F21B09B0FE009276B9 /* Project object */ = {
233 | isa = PBXProject;
234 | attributes = {
235 | LastUpgradeCheck = 1120;
236 | ORGANIZATIONNAME = LeanCloud;
237 | TargetAttributes = {
238 | 5CC504C71B0EFCBA004D0CB1 = {
239 | CreatedOnToolsVersion = 6.3.2;
240 | };
241 | 5CCAE0FA1B09B0FE009276B9 = {
242 | CreatedOnToolsVersion = 6.3.1;
243 | };
244 | };
245 | };
246 | buildConfigurationList = 5CCAE0F51B09B0FE009276B9 /* Build configuration list for PBXProject "LeanCloudFeedback" */;
247 | compatibilityVersion = "Xcode 3.2";
248 | developmentRegion = English;
249 | hasScannedForEncodings = 0;
250 | knownRegions = (
251 | English,
252 | en,
253 | "zh-Hans",
254 | );
255 | mainGroup = 5CCAE0F11B09B0FE009276B9;
256 | productRefGroup = 5CCAE0FC1B09B0FE009276B9 /* Products */;
257 | projectDirPath = "";
258 | projectRoot = "";
259 | targets = (
260 | 5CCAE0FA1B09B0FE009276B9 /* LeanCloudFeedback */,
261 | 5CC504C71B0EFCBA004D0CB1 /* UniversalFramework */,
262 | );
263 | };
264 | /* End PBXProject section */
265 |
266 | /* Begin PBXResourcesBuildPhase section */
267 | 5CCAE0F91B09B0FE009276B9 /* Resources */ = {
268 | isa = PBXResourcesBuildPhase;
269 | buildActionMask = 2147483647;
270 | files = (
271 | 703074C41B9ABA6D00FD796E /* feedback_bg_1@2x.png in Resources */,
272 | 703074E81B9ABDA400FD796E /* feedback_add_image@2x.png in Resources */,
273 | 703074C71B9ABA6D00FD796E /* feedback_btn_new@2x.png in Resources */,
274 | 703074C31B9ABA6D00FD796E /* feedback_back@2x.png in Resources */,
275 | 703074C81B9ABA6D00FD796E /* feedback_warning@2x.png in Resources */,
276 | 7000FCF21BEC9E450097C729 /* LeanCloudFeedback.strings in Resources */,
277 | 703074C51B9ABA6D00FD796E /* feedback_bg_2@2x.png in Resources */,
278 | 703074C61B9ABA6D00FD796E /* feedback_btn@2x.png in Resources */,
279 | );
280 | runOnlyForDeploymentPostprocessing = 0;
281 | };
282 | /* End PBXResourcesBuildPhase section */
283 |
284 | /* Begin PBXShellScriptBuildPhase section */
285 | 5CC504CB1B0EFCC0004D0CB1 /* ShellScript */ = {
286 | isa = PBXShellScriptBuildPhase;
287 | buildActionMask = 2147483647;
288 | files = (
289 | );
290 | inputPaths = (
291 | );
292 | outputPaths = (
293 | );
294 | runOnlyForDeploymentPostprocessing = 0;
295 | shellPath = /bin/sh;
296 | shellScript = "# This script is based on Jacob Van Order's answer on apple dev forums\n\n# https://devforums.apple.com/message/971277\n# See also http://spin.atomicobject.com/2011/12/13/building-a-universal-framework-for-ios/ for the start\n\n\n# To get this to work with a Xcode 6 Cocoa Touch Framework, create Framework\n# Then create a new Aggregate Target. Throw this script into a Build Script Phrase on the Aggregate\n\n\n######################\n# Options\n######################\n\nREVEAL_ARCHIVE_IN_FINDER=true\n\nFRAMEWORK_NAME=\"${PROJECT_NAME}\"\n\nSIMULATOR_LIBRARY_PATH=\"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework\"\n\nDEVICE_LIBRARY_PATH=\"${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework\"\n\nUNIVERSAL_LIBRARY_DIR=\"./build/${CONFIGURATION}-iphoneuniversal\"\n\nFRAMEWORK=\"${UNIVERSAL_LIBRARY_DIR}/${FRAMEWORK_NAME}.framework\"\n\n\n######################\n# Build Frameworks\n######################\n\nxcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator | echo\n\nxcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos | echo\n\n#xcodebuild -target ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" | echo\n\n#xcodebuild -target ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" | echo\n\n\n######################\n# Create directory for universal\n######################\n\nrm -rf \"${UNIVERSAL_LIBRARY_DIR}\"\n\nmkdir \"${UNIVERSAL_LIBRARY_DIR}\"\n\nmkdir \"${FRAMEWORK}\"\n\n\n######################\n# Copy files Framework\n######################\n\ncp -r \"${DEVICE_LIBRARY_PATH}/.\" \"${FRAMEWORK}\"\n\n\n######################\n# Make fat universal binary\n######################\n\nlipo \"${SIMULATOR_LIBRARY_PATH}/${FRAMEWORK_NAME}\" \"${DEVICE_LIBRARY_PATH}/${FRAMEWORK_NAME}\" -create -output \"${FRAMEWORK}/${FRAMEWORK_NAME}\" | echo\n\nchmod -x \"${FRAMEWORK}/${FRAMEWORK_NAME}\"\n\nopen \"${UNIVERSAL_LIBRARY_DIR}/\"\n######################\n# On Release, copy the result to desktop folder\n######################\n\n#if [ \"${CONFIGURATION}\" == \"Release\" ]; then\n#mkdir \"${HOME}/Desktop/${FRAMEWORK_NAME}-${CONFIGURATION}-iphoneuniversal/\"\n#cp -r \"${FRAMEWORK}\" \"${HOME}/Desktop/${FRAMEWORK_NAME}-${CONFIGURATION}-iphoneuniversal/\"\n#fi\n\n\n######################\n# If needed, open the Framework folder\n######################\n\n#if [ ${REVEAL_ARCHIVE_IN_FINDER} = true ]; then\n#if [ \"${CONFIGURATION}\" == \"Release\" ]; then\n#open \"${HOME}/Desktop/${FRAMEWORK_NAME}-${CONFIGURATION}-iphoneuniversal/\"\n#else\n#open \"${UNIVERSAL_LIBRARY_DIR}/\"\n#fi\n#fi";
297 | };
298 | 84898830CC4C08815C7F6A2D /* [CP] Check Pods Manifest.lock */ = {
299 | isa = PBXShellScriptBuildPhase;
300 | buildActionMask = 2147483647;
301 | files = (
302 | );
303 | inputPaths = (
304 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
305 | "${PODS_ROOT}/Manifest.lock",
306 | );
307 | name = "[CP] Check Pods Manifest.lock";
308 | outputPaths = (
309 | "$(DERIVED_FILE_DIR)/Pods-LeanCloudFeedback-checkManifestLockResult.txt",
310 | );
311 | runOnlyForDeploymentPostprocessing = 0;
312 | shellPath = /bin/sh;
313 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
314 | showEnvVarsInLog = 0;
315 | };
316 | 96A4361C0F19D5AEB1038786 /* [CP] Copy Pods Resources */ = {
317 | isa = PBXShellScriptBuildPhase;
318 | buildActionMask = 2147483647;
319 | files = (
320 | );
321 | inputPaths = (
322 | "${PODS_ROOT}/Target Support Files/Pods-LeanCloudFeedback/Pods-LeanCloudFeedback-resources.sh",
323 | "${PODS_ROOT}/AVOSCloud/AVOS/AVOSCloud/AVOSCloud_Art.inc",
324 | );
325 | name = "[CP] Copy Pods Resources";
326 | outputPaths = (
327 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AVOSCloud_Art.inc",
328 | );
329 | runOnlyForDeploymentPostprocessing = 0;
330 | shellPath = /bin/sh;
331 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LeanCloudFeedback/Pods-LeanCloudFeedback-resources.sh\"\n";
332 | showEnvVarsInLog = 0;
333 | };
334 | /* End PBXShellScriptBuildPhase section */
335 |
336 | /* Begin PBXSourcesBuildPhase section */
337 | 5CCAE0F61B09B0FE009276B9 /* Sources */ = {
338 | isa = PBXSourcesBuildPhase;
339 | buildActionMask = 2147483647;
340 | files = (
341 | 5CCAE12F1B09B323009276B9 /* LCUserFeedbackReply.m in Sources */,
342 | 5CCAE1251B09B323009276B9 /* LCUserFeedbackThread.m in Sources */,
343 | 5CCAE12B1B09B323009276B9 /* LCUserFeedbackReplyCell.m in Sources */,
344 | 70FB0B281B5E76D500FD8D70 /* LCUserFeedbackImageViewController.m in Sources */,
345 | 5CCAE1261B09B323009276B9 /* LCUserFeedbackViewController.m in Sources */,
346 | 5CCAE1371B09C1F2009276B9 /* LCUtils.m in Sources */,
347 | 5CCAE1241B09B323009276B9 /* LCUserFeedbackAgent.m in Sources */,
348 | 5CCAE1351B09C1F2009276B9 /* LCHttpClient.m in Sources */,
349 | );
350 | runOnlyForDeploymentPostprocessing = 0;
351 | };
352 | /* End PBXSourcesBuildPhase section */
353 |
354 | /* Begin XCBuildConfiguration section */
355 | 5CC504C81B0EFCBA004D0CB1 /* Debug */ = {
356 | isa = XCBuildConfiguration;
357 | buildSettings = {
358 | PRODUCT_NAME = "$(TARGET_NAME)";
359 | };
360 | name = Debug;
361 | };
362 | 5CC504C91B0EFCBA004D0CB1 /* Release */ = {
363 | isa = XCBuildConfiguration;
364 | buildSettings = {
365 | PRODUCT_NAME = "$(TARGET_NAME)";
366 | };
367 | name = Release;
368 | };
369 | 5CCAE10F1B09B0FE009276B9 /* Debug */ = {
370 | isa = XCBuildConfiguration;
371 | buildSettings = {
372 | ALWAYS_SEARCH_USER_PATHS = NO;
373 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
374 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
375 | CLANG_CXX_LIBRARY = "libc++";
376 | CLANG_ENABLE_MODULES = YES;
377 | CLANG_ENABLE_OBJC_ARC = YES;
378 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
379 | CLANG_WARN_BOOL_CONVERSION = YES;
380 | CLANG_WARN_COMMA = YES;
381 | CLANG_WARN_CONSTANT_CONVERSION = YES;
382 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
384 | CLANG_WARN_EMPTY_BODY = YES;
385 | CLANG_WARN_ENUM_CONVERSION = YES;
386 | CLANG_WARN_INFINITE_RECURSION = YES;
387 | CLANG_WARN_INT_CONVERSION = YES;
388 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
389 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
390 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
392 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
393 | CLANG_WARN_STRICT_PROTOTYPES = YES;
394 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
395 | CLANG_WARN_UNREACHABLE_CODE = YES;
396 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
397 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
398 | COPY_PHASE_STRIP = NO;
399 | CURRENT_PROJECT_VERSION = 1;
400 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
401 | ENABLE_STRICT_OBJC_MSGSEND = YES;
402 | ENABLE_TESTABILITY = YES;
403 | GCC_C_LANGUAGE_STANDARD = gnu99;
404 | GCC_DYNAMIC_NO_PIC = NO;
405 | GCC_NO_COMMON_BLOCKS = YES;
406 | GCC_OPTIMIZATION_LEVEL = 0;
407 | GCC_PREPROCESSOR_DEFINITIONS = (
408 | "DEBUG=1",
409 | "$(inherited)",
410 | );
411 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
414 | GCC_WARN_UNDECLARED_SELECTOR = YES;
415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
416 | GCC_WARN_UNUSED_FUNCTION = YES;
417 | GCC_WARN_UNUSED_VARIABLE = YES;
418 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
419 | MTL_ENABLE_DEBUG_INFO = YES;
420 | ONLY_ACTIVE_ARCH = YES;
421 | SDKROOT = iphoneos;
422 | TARGETED_DEVICE_FAMILY = "1,2";
423 | VERSIONING_SYSTEM = "apple-generic";
424 | VERSION_INFO_PREFIX = "";
425 | };
426 | name = Debug;
427 | };
428 | 5CCAE1101B09B0FE009276B9 /* Release */ = {
429 | isa = XCBuildConfiguration;
430 | buildSettings = {
431 | ALWAYS_SEARCH_USER_PATHS = NO;
432 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
434 | CLANG_CXX_LIBRARY = "libc++";
435 | CLANG_ENABLE_MODULES = YES;
436 | CLANG_ENABLE_OBJC_ARC = YES;
437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
438 | CLANG_WARN_BOOL_CONVERSION = YES;
439 | CLANG_WARN_COMMA = YES;
440 | CLANG_WARN_CONSTANT_CONVERSION = YES;
441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
443 | CLANG_WARN_EMPTY_BODY = YES;
444 | CLANG_WARN_ENUM_CONVERSION = YES;
445 | CLANG_WARN_INFINITE_RECURSION = YES;
446 | CLANG_WARN_INT_CONVERSION = YES;
447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
452 | CLANG_WARN_STRICT_PROTOTYPES = YES;
453 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
454 | CLANG_WARN_UNREACHABLE_CODE = YES;
455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
457 | COPY_PHASE_STRIP = NO;
458 | CURRENT_PROJECT_VERSION = 1;
459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
460 | ENABLE_NS_ASSERTIONS = NO;
461 | ENABLE_STRICT_OBJC_MSGSEND = YES;
462 | GCC_C_LANGUAGE_STANDARD = gnu99;
463 | GCC_NO_COMMON_BLOCKS = YES;
464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
466 | GCC_WARN_UNDECLARED_SELECTOR = YES;
467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
468 | GCC_WARN_UNUSED_FUNCTION = YES;
469 | GCC_WARN_UNUSED_VARIABLE = YES;
470 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
471 | MTL_ENABLE_DEBUG_INFO = NO;
472 | SDKROOT = iphoneos;
473 | TARGETED_DEVICE_FAMILY = "1,2";
474 | VALIDATE_PRODUCT = YES;
475 | VERSIONING_SYSTEM = "apple-generic";
476 | VERSION_INFO_PREFIX = "";
477 | };
478 | name = Release;
479 | };
480 | 5CCAE1121B09B0FE009276B9 /* Debug */ = {
481 | isa = XCBuildConfiguration;
482 | baseConfigurationReference = D94FDB034BF527C80C647B30 /* Pods-LeanCloudFeedback.debug.xcconfig */;
483 | buildSettings = {
484 | ALWAYS_SEARCH_USER_PATHS = NO;
485 | CODE_SIGN_IDENTITY = "";
486 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
487 | DEFINES_MODULE = YES;
488 | DYLIB_COMPATIBILITY_VERSION = 1;
489 | DYLIB_CURRENT_VERSION = 1;
490 | DYLIB_INSTALL_NAME_BASE = "@rpath";
491 | FRAMEWORK_SEARCH_PATHS = (
492 | "$(inherited)",
493 | "$(PROJECT_DIR)",
494 | );
495 | INFOPLIST_FILE = LeanCloudFeedback/Info.plist;
496 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
497 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
499 | MACH_O_TYPE = staticlib;
500 | PRODUCT_BUNDLE_IDENTIFIER = "com.leancloud.$(PRODUCT_NAME:rfc1034identifier)";
501 | PRODUCT_NAME = "$(TARGET_NAME)";
502 | SKIP_INSTALL = YES;
503 | };
504 | name = Debug;
505 | };
506 | 5CCAE1131B09B0FE009276B9 /* Release */ = {
507 | isa = XCBuildConfiguration;
508 | baseConfigurationReference = 59AC16570E133BA472B3DABB /* Pods-LeanCloudFeedback.release.xcconfig */;
509 | buildSettings = {
510 | ALWAYS_SEARCH_USER_PATHS = NO;
511 | CODE_SIGN_IDENTITY = "";
512 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
513 | DEFINES_MODULE = YES;
514 | DYLIB_COMPATIBILITY_VERSION = 1;
515 | DYLIB_CURRENT_VERSION = 1;
516 | DYLIB_INSTALL_NAME_BASE = "@rpath";
517 | FRAMEWORK_SEARCH_PATHS = (
518 | "$(inherited)",
519 | "$(PROJECT_DIR)",
520 | );
521 | INFOPLIST_FILE = LeanCloudFeedback/Info.plist;
522 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
523 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
525 | MACH_O_TYPE = staticlib;
526 | PRODUCT_BUNDLE_IDENTIFIER = "com.leancloud.$(PRODUCT_NAME:rfc1034identifier)";
527 | PRODUCT_NAME = "$(TARGET_NAME)";
528 | SKIP_INSTALL = YES;
529 | };
530 | name = Release;
531 | };
532 | /* End XCBuildConfiguration section */
533 |
534 | /* Begin XCConfigurationList section */
535 | 5CC504CA1B0EFCBA004D0CB1 /* Build configuration list for PBXAggregateTarget "UniversalFramework" */ = {
536 | isa = XCConfigurationList;
537 | buildConfigurations = (
538 | 5CC504C81B0EFCBA004D0CB1 /* Debug */,
539 | 5CC504C91B0EFCBA004D0CB1 /* Release */,
540 | );
541 | defaultConfigurationIsVisible = 0;
542 | defaultConfigurationName = Release;
543 | };
544 | 5CCAE0F51B09B0FE009276B9 /* Build configuration list for PBXProject "LeanCloudFeedback" */ = {
545 | isa = XCConfigurationList;
546 | buildConfigurations = (
547 | 5CCAE10F1B09B0FE009276B9 /* Debug */,
548 | 5CCAE1101B09B0FE009276B9 /* Release */,
549 | );
550 | defaultConfigurationIsVisible = 0;
551 | defaultConfigurationName = Release;
552 | };
553 | 5CCAE1111B09B0FE009276B9 /* Build configuration list for PBXNativeTarget "LeanCloudFeedback" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 5CCAE1121B09B0FE009276B9 /* Debug */,
557 | 5CCAE1131B09B0FE009276B9 /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | /* End XCConfigurationList section */
563 | };
564 | rootObject = 5CCAE0F21B09B0FE009276B9 /* Project object */;
565 | }
566 |
--------------------------------------------------------------------------------