Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVilem Duha <vilem.duha@gmail.com>2021-10-20 08:53:20 +0300
committerVilem Duha <vilem.duha@gmail.com>2021-10-20 08:53:31 +0300
commit79a0f96101e3e098f76ce22a88b320f6c831b7b5 (patch)
tree6305099ac7e26fe66e626d08735e7412cd445b29
parent01a4619f5fbb93de457b9cc15a2cf564f87e2b7a (diff)
BlenderKit: initial support for notifications
slight optimisation of the new asset bar
-rw-r--r--blenderkit/asset_bar_op.py42
-rw-r--r--blenderkit/bkit_oauth.py1
-rw-r--r--blenderkit/bl_ui_widgets/bl_ui_button.py5
-rw-r--r--blenderkit/bl_ui_widgets/bl_ui_image.py5
-rw-r--r--blenderkit/comments_utils.py78
-rw-r--r--blenderkit/icons.py1
-rw-r--r--blenderkit/search.py6
-rw-r--r--blenderkit/thumbnails/bell.pngbin0 -> 946 bytes
-rw-r--r--blenderkit/ui_panels.py119
9 files changed, 223 insertions, 34 deletions
diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py
index 5011ffa2..2a2ff4e8 100644
--- a/blenderkit/asset_bar_op.py
+++ b/blenderkit/asset_bar_op.py
@@ -15,7 +15,7 @@ import random
import math
import blenderkit
-from blenderkit import ui, paths, utils, search
+from blenderkit import ui, paths, utils, search, comments_utils
from bpy.props import (
IntProperty,
@@ -55,8 +55,7 @@ def get_area_height(self):
BL_UI_Widget.get_area_height = get_area_height
-
-def asset_bar_modal(self, context, event):
+def modal_inside(self,context,event):
ui_props = bpy.context.window_manager.blenderkitUI
if ui_props.turn_off:
ui_props.turn_off = False
@@ -110,6 +109,9 @@ def asset_bar_modal(self, context, event):
return {"PASS_THROUGH"}
+def asset_bar_modal(self, context, event):
+ return modal_inside(self,context,event)
+
def asset_bar_invoke(self, context, event):
if not self.on_invoke(context, event):
@@ -285,6 +287,11 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
for w in self.tooltip_widgets:
w.visible = True
+ def show_notifications(self, widget):
+ bpy.ops.wm.show_notifications()
+ if comments_utils.check_notifications_read():
+ widget.visible = False
+
def check_new_search_results(self, context):
sr = bpy.context.window_manager.get('search results')
if not hasattr(self, 'search_results_count'):
@@ -404,15 +411,13 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.button_close.set_location(self.bar_width - self.other_button_size, 0)
self.button_scroll_up.set_location(self.bar_width, 0)
- self.panel.set_location(self.panel.x, self.panel.y)
self.panel.width = self.bar_width
self.panel.height = self.bar_height
+ self.panel.set_location(self.panel.x, self.panel.y)
+
# to hide arrows accordingly
self.scroll_update()
- # self.init_tooltip()
- # self.hide_tooltip()
- # self.scroll_update()
def asset_button_init(self, asset_x, asset_y, button_idx):
ui_scale = bpy.context.preferences.view.ui_scale
@@ -460,6 +465,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
progress_bar.bg_color = (0.0, 1.0, 0.0, 0.3)
new_button.progress_bar = progress_bar
self.progress_bars.append(progress_bar)
+
+
+
# if result['downloaded'] > 0:
# ui_bgl.draw_rect(x, y, int(ui_props.thumb_size * result['downloaded'] / 100.0), 2, green)
@@ -496,14 +504,17 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.asset_buttons.append(new_button)
button_idx += 1
- self.button_close = BL_UI_Button(self.bar_width - self.other_button_size, -0, self.other_button_size,
+ self.button_close = BL_UI_Button(self.bar_width - self.other_button_size, -self.other_button_size, self.other_button_size,
self.other_button_size)
self.button_close.bg_color = button_bg_color
self.button_close.hover_bg_color = button_hover_color
- self.button_close.text = "X"
+ self.button_close.text = ""
+ img_fp = paths.get_addon_thumbnail_path('vs_rejected.png')
+ self.button_close.set_image(img_fp)
self.button_close.set_mouse_down(self.cancel_press)
self.widgets_panel.append(self.button_close)
+
scroll_width = 30
self.button_scroll_down = BL_UI_Button(-scroll_width, 0, scroll_width, self.bar_height)
self.button_scroll_down.bg_color = button_bg_color
@@ -529,6 +540,19 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.widgets_panel.append(self.button_scroll_up)
+ #notifications
+ if not comments_utils.check_notifications_read():
+
+ self.button_notifications = BL_UI_Button(self.bar_width - self.other_button_size, self.bar_height, self.other_button_size,
+ self.other_button_size)
+ self.button_notifications.bg_color = button_bg_color
+ self.button_notifications.hover_bg_color = button_hover_color
+ self.button_notifications.text = ""
+ img_fp = paths.get_addon_thumbnail_path('bell.png')
+ self.button_notifications.set_image(img_fp)
+ self.button_notifications.set_mouse_down(self.show_notifications)
+ self.widgets_panel.append(self.button_notifications)
+
self.update_images()
def position_and_hide_buttons(self):
diff --git a/blenderkit/bkit_oauth.py b/blenderkit/bkit_oauth.py
index e080845a..fa00e616 100644
--- a/blenderkit/bkit_oauth.py
+++ b/blenderkit/bkit_oauth.py
@@ -91,6 +91,7 @@ def write_tokens(auth_token, refresh_token, oauth_response):
props.report = ''
ui.add_report('BlenderKit Re-Login success')
search.get_profile()
+
categories.fetch_categories_thread(auth_token, force = False)
diff --git a/blenderkit/bl_ui_widgets/bl_ui_button.py b/blenderkit/bl_ui_widgets/bl_ui_button.py
index 3f7fe082..d8772e68 100644
--- a/blenderkit/bl_ui_widgets/bl_ui_button.py
+++ b/blenderkit/bl_ui_widgets/bl_ui_button.py
@@ -68,8 +68,9 @@ class BL_UI_Button(BL_UI_Widget):
def set_image(self, rel_filepath):
try:
- self.__image = bpy.data.images.load(rel_filepath, check_existing=True)
- self.__image.gl_load()
+ if self.__image is None or self.__image.filepath != rel_filepath:
+ self.__image = bpy.data.images.load(rel_filepath, check_existing=True)
+ self.__image.gl_load()
except:
pass
diff --git a/blenderkit/bl_ui_widgets/bl_ui_image.py b/blenderkit/bl_ui_widgets/bl_ui_image.py
index 7d69ad13..a11c52c5 100644
--- a/blenderkit/bl_ui_widgets/bl_ui_image.py
+++ b/blenderkit/bl_ui_widgets/bl_ui_image.py
@@ -21,8 +21,9 @@ class BL_UI_Image(BL_UI_Widget):
def set_image(self, rel_filepath):
try:
- self.__image = bpy.data.images.load(rel_filepath, check_existing=True)
- self.__image.gl_load()
+ if self.__image is None or self.__image.filepath != rel_filepath:
+ self.__image = bpy.data.images.load(rel_filepath, check_existing=True)
+ self.__image.gl_load()
except:
pass
diff --git a/blenderkit/comments_utils.py b/blenderkit/comments_utils.py
index 121beeb1..f91e151f 100644
--- a/blenderkit/comments_utils.py
+++ b/blenderkit/comments_utils.py
@@ -27,8 +27,10 @@ import logging
bk_logger = logging.getLogger('blenderkit')
-def upload_comment_thread(url, comment='', headers=None):
+def upload_comment_thread(url, comment='', api_key=None):
''' Upload rating thread function / disconnected from blender data.'''
+ headers = utils.get_headers(api_key)
+
bk_logger.debug('upload comment ' + comment)
# rating_url = url + rating_name + '/'
@@ -55,8 +57,10 @@ def upload_comment_thread(url, comment='', headers=None):
# print('ratings upload failed: %s' % str(e))
-def upload_comment_flag_thread(url, comment_id='', flag='like', headers=None):
+def upload_comment_flag_thread(url, comment_id='', flag='like', api_key=None):
''' Upload rating thread function / disconnected from blender data.'''
+ headers = utils.get_headers(api_key)
+
bk_logger.debug('upload comment flag' + str(comment_id))
# rating_url = url + rating_name + '/'
@@ -74,10 +78,10 @@ def upload_comment_flag_thread(url, comment_id='', flag='like', headers=None):
# print('ratings upload failed: %s' % str(e))
-def send_comment_to_thread(url, comment, headers):
+def send_comment_to_thread(url, comment, api_key):
'''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_thread, args=(url, comment, headers))
+ thread = threading.Thread(target=upload_comment_thread, args=(url, comment, api_key))
thread.start()
@@ -97,9 +101,9 @@ def get_comments_local(asset_id):
return None
-def get_comments(asset_id, headers):
+def get_comments(asset_id, api_key):
'''
- Retrieve ratings from BlenderKit server. Can be run from a thread
+ Retrieve comments from BlenderKit server. Can be run from a thread
Parameters
----------
asset_id
@@ -109,6 +113,8 @@ def get_comments(asset_id, headers):
-------
ratings - dict of type:value ratings
'''
+ headers = utils.get_headers(api_key)
+
url = paths.get_api_url() + 'comments/assets-uuidasset/' + asset_id + '/'
params = {}
r = rerequests.get(url, params=params, verify=True, headers=headers)
@@ -125,3 +131,63 @@ def get_comments(asset_id, headers):
# # store empty ratings too, so that server isn't checked repeatedly
# tasks_queue.add_task((store_rating_local_empty,(asset_id,)))
# return ratings
+
+def store_notifications_local(notifications):
+ bpy.context.window_manager['bkit notifications'] = notifications
+
+def check_notifications_read():
+ '''checks if all notifications were already read, and removes them if so'''
+ all_read = True
+ notifications = bpy.context.window_manager.get('bkit notifications')
+ if notifications is None:
+ return True
+ for n in notifications:
+ if n['unread'] == 1:
+ all_read = False
+ return False
+ bpy.context.window_manager['bkit notifications'] = None
+ return True
+
+def get_notifications(api_key):
+ '''
+ Retrieve ratings from BlenderKit server. Can be run from a thread
+ Parameters
+ ----------
+ asset_id
+ headers
+
+ Returns
+ -------
+ ratings - dict of type:value ratings
+ '''
+ headers = utils.get_headers(api_key)
+
+ url = paths.get_api_url() + 'notifications/unread/'
+ params = {}
+ r = rerequests.get(url, params=params, verify=True, headers=headers)
+ if r is None:
+ return
+ if r.status_code == 200:
+ rj = r.json()
+ print(rj)
+ # store notifications - send them to task queue
+ tasks_queue.add_task((store_notifications_local, ([rj])))
+
+
+def mark_notification_read(api_key, notification_id):
+ '''
+ mark notification as read
+ '''
+ headers = utils.get_headers(api_key)
+
+ url = paths.get_api_url() + f'notifications/mark-as-read/{notification_id}/'
+ params = {}
+ r = rerequests.get(url, params=params, verify=True, headers=headers)
+ if r is None:
+ return
+ # if r.status_code == 200:
+ # rj = r.json()
+ # # store notifications - send them to task queue
+ # print(rj)
+ # tasks_queue.add_task((mark_notification_read_local, ([notification_id])))
+
diff --git a/blenderkit/icons.py b/blenderkit/icons.py
index 09a1ba19..379062ed 100644
--- a/blenderkit/icons.py
+++ b/blenderkit/icons.py
@@ -33,6 +33,7 @@ icons_read = {
'royalty_free.png': 'royalty_free',
'filter.png': 'filter',
'filter_active.png': 'filter_active',
+ 'bell.png': 'bell',
}
verification_icons = {
diff --git a/blenderkit/search.py b/blenderkit/search.py
index 83729d1b..9c0e5ac2 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
from blenderkit import paths, utils, categories, ui, colors, bkit_oauth, version_checker, tasks_queue, rerequests, \
- resolutions, image_utils, ratings_utils
+ resolutions, image_utils, ratings_utils, comments_utils
import blenderkit
from bpy.app.handlers import persistent
@@ -199,7 +199,6 @@ def fetch_server_data():
if bpy.context.window_manager.get('bkit_categories') is None:
categories.fetch_categories_thread(api_key, force=False)
-
first_time = True
last_clipboard = ''
@@ -838,6 +837,9 @@ def get_profile():
a = bpy.context.window_manager.get('bkit profile')
thread = threading.Thread(target=fetch_profile, args=(preferences.api_key,), daemon=True)
thread.start()
+ if utils.experimental_enabled():
+ comments_utils.get_notifications(preferences.api_key)
+
return a
diff --git a/blenderkit/thumbnails/bell.png b/blenderkit/thumbnails/bell.png
new file mode 100644
index 00000000..2b724a26
--- /dev/null
+++ b/blenderkit/thumbnails/bell.png
Binary files differ
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index af8f1993..f7dce680 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -592,6 +592,94 @@ class VIEW3D_PT_blenderkit_profile(Panel):
icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS
+class MarkNotificationRead(bpy.types.Operator):
+ """Visit subcategory"""
+ bl_idname = "wm.blenderkit_mark_notification_read"
+ bl_label = "Mark notification as read"
+ bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
+
+ notification_id: bpy.props.IntProperty(
+ name="Id",
+ description="notification id",
+ default=-1)
+
+ @classmethod
+ def poll(cls, context):
+ return True
+
+ def execute(self, context):
+ notifications = bpy.context.window_manager['bkit notifications']
+ for n in notifications:
+ if n['id'] == self.notification_id:
+ n['unread'] = 0
+ comments_utils.check_notifications_read()
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ api_key = user_preferences.api_key
+ comments_utils.mark_notification_read(api_key,self.notification_id)
+
+ return {'FINISHED'}
+
+
+def draw_notification(self, notification, width = 600):
+ layout = self.layout
+ box = layout.box()
+
+ firstline = f"user {notification['actor']['id']} {notification['verb']}"
+ 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.notification_id = notification['id']
+ utils.label_multiline(box, text=notification['description'], width = width)
+
+def draw_notifications(self, context, width = 600):
+ layout = self.layout
+ notifications = bpy.context.window_manager.get('bkit notifications')
+ if notifications is not None:
+ for notification in notifications:
+ if notification['unread'] == 1:
+ draw_notification(self,notification, width = width)
+
+class ShowNotifications(bpy.types.Operator):
+ """Show notifications"""
+ bl_idname = "wm.show_notifications"
+ bl_label = "Show BlenderKit notifications"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ notification_id: bpy.props.IntProperty(
+ name="Id",
+ description="notification id",
+ default=-1)
+
+ @classmethod
+ def poll(cls, context):
+ return True
+ def draw(self, context):
+ draw_notifications(self,context, width = 600)
+
+ def execute(self, context):
+ wm = bpy.context.window_manager
+ return wm.invoke_popup(self, width=600)
+
+class VIEW3D_PT_blenderkit_notifications(Panel):
+ bl_category = "BlenderKit"
+ bl_idname = "VIEW3D_PT_blenderkit_notifications"
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_label = "BlenderKit Notifications"
+
+ @classmethod
+ def poll(cls, context):
+ notifications = bpy.context.window_manager.get('bkit notifications')
+ if notifications is not None and len(notifications) > 0:
+ return True
+ return False
+ def draw(self,context):
+ draw_notifications(self,context)
+
+
+
+
class VIEW3D_PT_blenderkit_login(Panel):
bl_category = "BlenderKit"
bl_idname = "VIEW3D_PT_blenderkit_login"
@@ -1944,27 +2032,25 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
else:
role_text = ""
box.label(text=f"{comment['submitDate']} - {comment['userName']}{role_text}")
- utils.label_multiline(box, text=comment['comment'], width = width)
+ utils.label_multiline(box, text=comment['comment'], width=width)
removal = False
likes = 0
dislikes = 0
for l in comment['flags']:
if l['flag'] == 'like':
- likes +=1
+ likes += 1
if l['flag'] == 'dislike':
- dislikes +=1
+ dislikes += 1
if l['flag'] == 'removal':
removal = True
row = box.row()
- row.label(text = str(likes), icon = 'TRIA_UP')
- row.label(text = str(dislikes), icon = 'TRIA_DOWN')
+ row.label(text=str(likes), icon='TRIA_UP')
+ row.label(text=str(dislikes), icon='TRIA_DOWN')
if removal:
- row.label(text = '', icon = 'ERROR')
-
-
- #box.label(text=str(comment['flags']))
+ row.label(text='', icon='ERROR')
+ # box.label(text=str(comment['flags']))
def draw(self, context):
layout = self.layout
@@ -1997,7 +2083,6 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
for comment in self.comments:
self.draw_comment(context, layout, comment)
-
def execute(self, context):
wm = context.window_manager
ui_props = context.window_manager.blenderkitUI
@@ -2031,10 +2116,9 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
if utils.profile_is_validator() and bpy.app.debug_value == 2:
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
api_key = user_preferences.api_key
- headers = utils.get_headers(api_key)
comments = comments_utils.get_comments_local(asset_data['assetBaseId'])
if comments is None:
- comments_utils.get_comments(asset_data['assetBaseId'], headers)
+ comments_utils.get_comments(asset_data['assetBaseId'], api_key)
comments = bpy.context.window_manager.get('asset comments', {})
self.comments = comments.get(asset_data['assetBaseId'], [])
@@ -2332,9 +2416,15 @@ def header_search_draw(self, context):
elif ui_props.asset_type == 'HDR':
layout.popover(panel="VIEW3D_PT_blenderkit_advanced_HDR_search", text="", icon_value=icon_id)
+ notifications = bpy.context.window_manager.get('bkit notifications')
+ if notifications is not None and len(notifications) > 0:
+ layout.operator('wm.show_notifications', text="", icon_value=pcoll['bell'].icon_id)
+ # layout.popover(panel="VIEW3D_PT_blenderkit_notifications", text="", icon_value=pcoll['bell'].icon_id)
+
if utils.profile_is_validator():
search_props = utils.get_search_props()
- layout.prop(search_props, 'search_verification_status', text ='')
+ layout.prop(search_props, 'search_verification_status', text='')
+
def ui_message(title, message):
def draw_message(self, context):
@@ -2352,6 +2442,7 @@ classes = (
SetCategoryOperator,
VIEW3D_PT_blenderkit_profile,
VIEW3D_PT_blenderkit_login,
+ VIEW3D_PT_blenderkit_notifications,
VIEW3D_PT_blenderkit_unified,
VIEW3D_PT_blenderkit_advanced_model_search,
VIEW3D_PT_blenderkit_advanced_material_search,
@@ -2369,6 +2460,8 @@ classes = (
UrlPopupDialog,
ClosePopupButton,
BlenderKitWelcomeOperator,
+ MarkNotificationRead,
+ ShowNotifications,
)