diff options
author | Vilem Duha <vilem.duha@gmail.com> | 2021-10-23 17:38:01 +0300 |
---|---|---|
committer | Vilem Duha <vilem.duha@gmail.com> | 2021-10-24 20:11:15 +0300 |
commit | 490e63d2a5063c85ecb9eb14e35a98db7c96e5d3 (patch) | |
tree | d78d7242df343744c4dc4ff81ef35cf445b07c57 | |
parent | 996ff7f869357823867e3b02542418852e85aabd (diff) |
BlenderKit: work on comments and notifications
Substantial progress on both, mainly layout and adaptation to new API.
Comments now show threads and can be liked/disliked from addon
Notifications now can open the related asset to see discussion.
-rw-r--r-- | blenderkit/__init__.py | 6 | ||||
-rw-r--r-- | blenderkit/asset_bar_op.py | 2 | ||||
-rw-r--r-- | blenderkit/comments_utils.py | 66 | ||||
-rw-r--r-- | blenderkit/download.py | 2 | ||||
-rw-r--r-- | blenderkit/search.py | 13 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 114 | ||||
-rw-r--r-- | blenderkit/utils.py | 17 |
7 files changed, 168 insertions, 52 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 4a25c9d6..3b35b912 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -1730,12 +1730,18 @@ class BlenderKitAddonPreferences(AddonPreferences): thumb_size: IntProperty(name="Assetbar thumbnail Size", default=96, min=-1, max=256) + #counts usages so it can encourage user after some time to do things. asset_counter: IntProperty(name="Usage Counter", description="Counts usages so it asks for registration only after reaching a limit", default=0, min=0, max=20000) + notifications_counter: IntProperty( + name='Notifications Counter', + description='count users notifications', + default=0, + ) # this is now made obsolete by the new popup upon registration -ensures the user knows about the first search. # first_run: BoolProperty( # name="First run", diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py index d13c3767..3403625c 100644 --- a/blenderkit/asset_bar_op.py +++ b/blenderkit/asset_bar_op.py @@ -788,7 +788,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): def search_more(self): sro = bpy.context.window_manager.get('search results orig') if sro is None: - return; + return if sro.get('next') is None: return search_props = utils.get_search_props() diff --git a/blenderkit/comments_utils.py b/blenderkit/comments_utils.py index cf6ccd06..23a17405 100644 --- a/blenderkit/comments_utils.py +++ b/blenderkit/comments_utils.py @@ -51,13 +51,13 @@ def upload_comment_thread(url, comment='', api_key=None): # try: r = rerequests.put(url, data=data, verify=True, headers=headers) print(r) - print(dir(r)) + # print(dir(r)) print(r.text) # except requests.exceptions.RequestException as e: # print('ratings upload failed: %s' % str(e)) -def upload_comment_flag_thread(url, comment_id='', flag='like', api_key=None): +def upload_comment_flag_thread( asset_id = '', comment_id='', flag='like', api_key=None): ''' Upload rating thread function / disconnected from blender data.''' headers = utils.get_headers(api_key) @@ -68,15 +68,21 @@ def upload_comment_flag_thread(url, comment_id='', flag='like', api_key=None): "comment": comment_id, "flag": flag, } + url = paths.get_api_url() + 'comments/feedback/' # try: r = rerequests.post(url, data=data, verify=True, headers=headers) - print(r) - print(dir(r)) print(r.text) - # except requests.exceptions.RequestException as e: - # print('ratings upload failed: %s' % str(e)) + #here it's important we read back, so likes are updated accordingly: + get_comments(asset_id, api_key) + + +def send_comment_flag_to_thread(asset_id = '', comment_id='', flag='like', api_key = None): + '''Sens rating into thread rating, main purpose is for tasks_queue. + One function per property to avoid lost data due to stashing.''' + thread = threading.Thread(target=upload_comment_flag_thread, args=(asset_id, comment_id, flag, api_key)) + thread.start() def send_comment_to_thread(url, comment, api_key): '''Sens rating into thread rating, main purpose is for tasks_queue. @@ -100,6 +106,14 @@ def get_comments_local(asset_id): return comments return None +def get_comments_thread(asset_id, api_key): + thread = threading.Thread(target=get_comments, args=([asset_id, api_key]), daemon=True) + thread.start() + + +def get_comments_thread(asset_id, api_key): + thread = threading.Thread(target=get_comments, args=([asset_id, api_key]), daemon=True) + thread.start() def get_comments(asset_id, api_key): ''' @@ -120,11 +134,12 @@ def get_comments(asset_id, api_key): r = rerequests.get(url, params=params, verify=True, headers=headers) if r is None: return + print(r.status_code) if r.status_code == 200: rj = r.json() - comments = [] # store comments - send them to task queue - + # print('retrieved comments') + # print(rj) tasks_queue.add_task((store_comments_local, (asset_id, rj['results']))) # if len(rj['results'])==0: @@ -132,20 +147,21 @@ def get_comments(asset_id, api_key): # tasks_queue.add_task((store_rating_local_empty,(asset_id,))) # return ratings + +def store_notifications_count_local(all_count): + '''Store total count of notifications on server in preferences''' + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + user_preferences.notifications_counter = all_count + def store_notifications_local(notifications): + '''Store notifications in Blender''' bpy.context.window_manager['bkit notifications'] = notifications -def count_unread_notifications(): - notifications = bpy.context.window_manager.get('bkit notifications') - if notifications is None: - return 0 - unread = 0 - for n in notifications: - - if n['unread'] == 1: - unread +=1 - print('counted', unread) - return unread +def count_all_notifications(): + '''Return count of all notifications on server''' + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + return user_preferences.notifications_counter + def check_notifications_read(): '''checks if all notifications were already read, and removes them if so''' @@ -158,14 +174,14 @@ def check_notifications_read(): bpy.context.window_manager['bkit notifications'] = None return True -def get_notifications(api_key, unread_count = 1000): +def get_notifications(api_key, all_count = 1000): ''' Retrieve notifications from BlenderKit server. Can be run from a thread. Parameters ---------- api_key - unread_count + all_count Returns ------- @@ -174,14 +190,16 @@ def get_notifications(api_key, unread_count = 1000): params = {} - url = paths.get_api_url() + 'notifications/api/unread_count/' + url = paths.get_api_url() + 'notifications/all_count/' r = rerequests.get(url, params=params, verify=True, headers=headers) if r.status_code ==200: rj = r.json() + print(rj) # no new notifications? - if unread_count >= rj['unreadCount']: + if all_count >= rj['allCount']: + tasks_queue.add_task((store_notifications_count_local, ([rj['allCount']]))) + return - print('notifications', unread_count, rj['unreadCount']) url = paths.get_api_url() + 'notifications/unread/' r = rerequests.get(url, params=params, verify=True, headers=headers) if r is None: diff --git a/blenderkit/download.py b/blenderkit/download.py index 9fb48073..f33e636f 100644 --- a/blenderkit/download.py +++ b/blenderkit/download.py @@ -1421,7 +1421,7 @@ class BlenderkitDownloadOperator(bpy.types.Operator): # if self.close_window: # context.window.cursor_warp(event.mouse_x-1000, event.mouse_y - 1000); - print(self.asset_base_id) + # print(self.asset_base_id) wm = context.window_manager # only make a pop up in case of switching resolutions if self.invoke_resolution: diff --git a/blenderkit/search.py b/blenderkit/search.py index 0464f511..fc716fdc 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -108,8 +108,8 @@ def refresh_notifications_timer(): ''' this timer gets notifications.''' preferences = bpy.context.preferences.addons['blenderkit'].preferences fetch_server_data() - unread_notifications_count = comments_utils.count_unread_notifications() - comments_utils.get_notifications(preferences.api_key, unread_count = unread_notifications_count) + all_notifications_count = comments_utils.count_all_notifications() + comments_utils.get_notifications(preferences.api_key, all_count = all_notifications_count) return 300 @@ -730,7 +730,8 @@ def write_gravatar(a_id, gravatar_path): adata['gravatarImg'] = gravatar_path -def fetch_gravatar(adata): +def fetch_gravatar(adata = None): + ''' Gets avatars from blenderkit server Parameters @@ -739,7 +740,7 @@ def fetch_gravatar(adata): ''' # utils.p('fetch gravatar') - + print(adata) # fetch new avatars if available already if adata.get('avatar128') is not None: avatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['id'] + '.jpg' @@ -784,7 +785,9 @@ fetching_gravatars = {} def get_author(r): - ''' Writes author info (now from search results) and fetches gravatar if needed.''' + ''' Writes author info (now from search results) and fetches gravatar if needed. + this is now tweaked to be able to get authors from + ''' global fetching_gravatars a_id = str(r['author']['id']) diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index add64653..d94d483d 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -593,7 +593,7 @@ class VIEW3D_PT_blenderkit_profile(Panel): class MarkNotificationRead(bpy.types.Operator): - """Visit subcategory""" + """Mark notification as read here and also on BlenderKit server""" bl_idname = "wm.blenderkit_mark_notification_read" bl_label = "Mark notification as read" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} @@ -620,17 +620,70 @@ class MarkNotificationRead(bpy.types.Operator): return {'FINISHED'} +class LikeComment(bpy.types.Operator): + """Mark notification as read here and also on BlenderKit server""" + bl_idname = "wm.blenderkit_like_comment" + bl_label = "BlenderKit like/dislike comment" + bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} + + asset_id: StringProperty( + name="Asset Base Id", + description="Unique id of the asset (hidden)", + default="", + options={'SKIP_SAVE'}) + + comment_id: bpy.props.IntProperty( + name="Id", + description="comment id", + default=-1) + + flag: bpy.props.StringProperty( + name="flag", + description="Like/dislike comment", + default="like") + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + api_key = user_preferences.api_key + comments_utils.send_comment_flag_to_thread(asset_id = self.asset_id, comment_id=self.comment_id, flag=self.flag, api_key = api_key) + return {'FINISHED'} + + def draw_notification(self, notification, width = 600): layout = self.layout box = layout.box() - - firstline = f"user {notification['actor']['id']} {notification['verb']}" + print(notification.to_dict()) + firstline = f"{notification['actor']['string']} {notification['verb']} {notification['target']['string']}" box1 = box.box() row = box1.row() utils.label_multiline(row, text=firstline, width = width) - op = row.operator("wm.blenderkit_mark_notification_read", text="", icon='CHECKMARK') + + + + op = row.operator("wm.blenderkit_mark_notification_read", text="", icon='CANCEL') op.notification_id = notification['id'] - utils.label_multiline(box, text=notification['description'], width = width) + if notification['description']: + rows = utils.label_multiline(box, text=notification['description'], width = width) + split = rows[-1].split(factor = 0.8) + + else: + row = layout.row() + split = row.split(factor = 0.8) + split.label(text = '') + split = split.split() + if notification['target']: + # row = layout.row() + # split = row.split(factor=.8) + # split.label(text='') + # split = split.split() + op = split.operator('wm.blenderkit_url', text='Open page', icon='GREASEPENCIL') + op.tooltip = 'Open the browser on the asset page to comment' + op.url = paths.get_bkit_url() + notification['target']['url'] def draw_notifications(self, context, width = 600): layout = self.layout @@ -1435,13 +1488,13 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): row = layout.row() row.operator_context = 'INVOKE_DEFAULT' - op = layout.operator('wm.blenderkit_fast_metadata', text='Edit Metadata') + op = layout.operator('wm.blenderkit_fast_metadata', text='Edit Metadata',icon='GREASEPENCIL') op.asset_id = asset_data['id'] op.asset_type = asset_data['assetType'] if author_id == str(profile['user']['id']): row.operator_context = 'EXEC_DEFAULT' - op = layout.operator('wm.blenderkit_url', text='Edit Metadata (browser)') + op = layout.operator('wm.blenderkit_url', text='Edit Metadata (browser)', icon='GREASEPENCIL') op.url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS + f"/{asset_data['assetBaseId']}/?edit#" row.operator_context = 'INVOKE_DEFAULT' @@ -2036,7 +2089,13 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): op = name_row.operator('view3d.close_popup_button', text='', icon='CANCEL') def draw_comment(self, context, layout, comment, width=330): - box = layout.box() + row = layout.row() + # print(comment) + if comment['level']>0: + split = row.split(factor = 0.05 * comment['level']) + split.label(text='') + row = split.split() + box = row.box() box.emboss = 'NORMAL' row = box.row() split = row.split(factor = 0.8) @@ -2045,7 +2104,9 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): role_text = f" - moderator" else: role_text = "" - split.label(text=f"{comment['submitDate']} - {comment['userName']}{role_text}") + row = split.row() + row.enabled = False + row.label(text=f"{comment['submitDate']} - {comment['userName']}{role_text}") removal = False likes = 0 dislikes = 0 @@ -2058,13 +2119,33 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): removal = True # row = box.row() split1 = split.split() - split1.label(text=str(likes), icon='TRIA_UP') - split1.label(text=str(dislikes), icon='TRIA_DOWN') + # split1.emboss = 'NONE' + op = split1.operator('wm.blenderkit_like_comment', text=str(likes), icon='TRIA_UP') + op.asset_id = self.asset_data['assetBaseId'] + op.comment_id = comment['id'] + op.flag = 'like' + op = split1.operator('wm.blenderkit_like_comment', text=str(dislikes), icon='TRIA_DOWN') + op.asset_id = self.asset_data['assetBaseId'] + op.comment_id = comment['id'] + op.flag = 'dislike' + # op = split1.operator('wm.blenderkit_like_comment', text='report', icon='ERROR') + # op.asset_id = self.asset_data['assetBaseId'] + # op.comment_id = comment['id'] + # op.flag = 'removal' if removal: + row.alert = True row.label(text='', icon='ERROR') - utils.label_multiline(box, text=comment['comment'], width=width) + rows = utils.label_multiline(box, text=comment['comment'], width=width * (1 - 0.05 * comment['level'])) + + row = rows[-1] + split = row.split(factor=.8) + split.label(text='') + split = split.split() + op = split.operator('wm.blenderkit_url', text='Reply', icon='GREASEPENCIL') + op.tooltip = 'Open the browser on the asset page to comment' + op.url = paths.get_bkit_url() + f"/asset-gallery-detail/{self.asset_data['id']}/" # box.label(text=str(comment['flags'])) def draw(self, context): @@ -2094,9 +2175,11 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): tip_box.label(text=self.tip) # comments if utils.profile_is_validator() and bpy.app.debug_value == 2: + comments = bpy.context.window_manager.get('asset comments', {}) + self.comments = comments.get(self.asset_data['assetBaseId'], []) if self.comments is not None: for comment in self.comments: - self.draw_comment(context, layout, comment) + self.draw_comment(context, layout, comment, width = self.width) def execute(self, context): wm = context.window_manager @@ -2132,8 +2215,8 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): user_preferences = bpy.context.preferences.addons['blenderkit'].preferences api_key = user_preferences.api_key comments = comments_utils.get_comments_local(asset_data['assetBaseId']) - if comments is None: - comments_utils.get_comments(asset_data['assetBaseId'], api_key) + # if comments is None: + comments_utils.get_comments_thread(asset_data['assetBaseId'], api_key) comments = bpy.context.window_manager.get('asset comments', {}) self.comments = comments.get(asset_data['assetBaseId'], []) @@ -2478,6 +2561,7 @@ classes = ( ClosePopupButton, BlenderKitWelcomeOperator, MarkNotificationRead, + LikeComment, ShowNotifications, ) diff --git a/blenderkit/utils.py b/blenderkit/utils.py index d09c369b..5bbe96af 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -927,10 +927,11 @@ def label_multiline(layout, text='', icon='NONE', width=-1, max_lines=10): Returns ------- - True if max_lines was overstepped + rows of the text(to add extra elements) ''' + rows = [] if text.strip() == '': - return + return [layout.row()] text = text.replace('\r\n', '\n') lines = text.split('\n') if width > 0: @@ -946,7 +947,9 @@ def label_multiline(layout, text='', icon='NONE', width=-1, max_lines=10): if i < 1: i = threshold l1 = l[:i] - layout.label(text=l1, icon=icon) + row = layout.row() + row.label(text=l1, icon=icon) + rows.append(row) icon = 'NONE' l = l[i:].lstrip() li += 1 @@ -954,10 +957,12 @@ def label_multiline(layout, text='', icon='NONE', width=-1, max_lines=10): break; if li > max_lines: break; - layout.label(text=l, icon=icon) + row = layout.row() + row.label(text=l, icon=icon) + rows.append(row) icon = 'NONE' - if li > max_lines: - return True + # if li > max_lines: + return rows def trace(): |